feat: add configuration-center module.
This commit is contained in:
parent
d5f49531e9
commit
3ba8fc9759
@ -20,6 +20,7 @@ import { LoggerModule } from 'nestjs-pino';
|
|||||||
import { EtcdModule } from 'nestjs-etcd';
|
import { EtcdModule } from 'nestjs-etcd';
|
||||||
import pinoPretty from 'pino-pretty';
|
import pinoPretty from 'pino-pretty';
|
||||||
import { fromPairs, map, pipe, toPairs } from 'ramda';
|
import { fromPairs, map, pipe, toPairs } from 'ramda';
|
||||||
|
import { ConfigurationsModule } from './configurations/configurations.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -106,6 +107,7 @@ import { fromPairs, map, pipe, toPairs } from 'ramda';
|
|||||||
}),
|
}),
|
||||||
WebhooksModule,
|
WebhooksModule,
|
||||||
CommonsModule,
|
CommonsModule,
|
||||||
|
ConfigurationsModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService, AppResolver],
|
providers: [AppService, AppResolver],
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Global, Module } from '@nestjs/common';
|
||||||
import { PasswordConverter } from './services/password-converter';
|
import { PasswordConverter } from './services/password-converter';
|
||||||
import { RedisMutexModule } from './redis-mutex/redis-mutex.module';
|
import { RedisMutexModule } from './redis-mutex/redis-mutex.module';
|
||||||
import { AuthModule } from '@nestjs-lib/auth';
|
import { AuthModule } from '@nestjs-lib/auth';
|
||||||
|
|
||||||
|
@Global()
|
||||||
@Module({
|
@Module({
|
||||||
imports: [RedisMutexModule, AuthModule],
|
imports: [RedisMutexModule, AuthModule],
|
||||||
providers: [PasswordConverter],
|
providers: [PasswordConverter],
|
||||||
|
11
src/configurations/configurations.module.ts
Normal file
11
src/configurations/configurations.module.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Configuration } from './entities/configuration.entity';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigurationsService } from './configurations.service';
|
||||||
|
import { ConfigurationsResolver } from './configurations.resolver';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Configuration])],
|
||||||
|
providers: [ConfigurationsResolver, ConfigurationsService],
|
||||||
|
})
|
||||||
|
export class ConfigurationsModule {}
|
24
src/configurations/configurations.resolver.spec.ts
Normal file
24
src/configurations/configurations.resolver.spec.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { ConfigurationsResolver } from './configurations.resolver';
|
||||||
|
import { ConfigurationsService } from './configurations.service';
|
||||||
|
|
||||||
|
describe('ConfigurationsResolver', () => {
|
||||||
|
let resolver: ConfigurationsResolver;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ConfigurationsService,
|
||||||
|
useValue: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
resolver = module.get<ConfigurationsResolver>(ConfigurationsResolver);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(resolver).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
39
src/configurations/configurations.resolver.ts
Normal file
39
src/configurations/configurations.resolver.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { UnprocessableEntityException } from '@nestjs/common';
|
||||||
|
import { GetConfigurationArgs } from './dto/get-configuration.args';
|
||||||
|
import { SetConfigurationInput } from './dto/set-configuration.input';
|
||||||
|
import { Resolver, Mutation, Args, Query } from '@nestjs/graphql';
|
||||||
|
import { ConfigurationsService } from './configurations.service';
|
||||||
|
import { Configuration } from './entities/configuration.entity';
|
||||||
|
import { any, pipe, values } from 'ramda';
|
||||||
|
import { AccountRole, Roles } from '@nestjs-lib/auth';
|
||||||
|
|
||||||
|
@Roles(AccountRole.admin, AccountRole.super)
|
||||||
|
@Resolver(() => Configuration)
|
||||||
|
export class ConfigurationsResolver {
|
||||||
|
constructor(private readonly configurationsService: ConfigurationsService) {}
|
||||||
|
|
||||||
|
@Mutation(() => Configuration)
|
||||||
|
setConfiguration(
|
||||||
|
@Args('setConfigurationInput', { type: () => SetConfigurationInput })
|
||||||
|
setConfigurationInput: SetConfigurationInput,
|
||||||
|
) {
|
||||||
|
return this.configurationsService.setConfiguration(setConfigurationInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query(() => Configuration, { nullable: true })
|
||||||
|
getConfiguration(
|
||||||
|
@Args()
|
||||||
|
getConfigurationArgs: GetConfigurationArgs,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
pipe(
|
||||||
|
values,
|
||||||
|
any((value) => !value),
|
||||||
|
)(getConfigurationArgs)
|
||||||
|
) {
|
||||||
|
throw new UnprocessableEntityException('Must pass a parameter');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.configurationsService.findOneByConditions(getConfigurationArgs);
|
||||||
|
}
|
||||||
|
}
|
44
src/configurations/configurations.service.spec.ts
Normal file
44
src/configurations/configurations.service.spec.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { ConfigurationsService } from './configurations.service';
|
||||||
|
import { Configuration } from './entities/configuration.entity';
|
||||||
|
import { IsNull } from 'typeorm';
|
||||||
|
|
||||||
|
describe('ConfigurationsService', () => {
|
||||||
|
let service: ConfigurationsService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
ConfigurationsService,
|
||||||
|
{
|
||||||
|
provide: getRepositoryToken(Configuration),
|
||||||
|
useValue: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<ConfigurationsService>(ConfigurationsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findOneByConditions', () => {
|
||||||
|
it('should select by projectId only', async () => {
|
||||||
|
const entity = new Configuration();
|
||||||
|
const findOne = jest.fn((_) => Promise.resolve(entity));
|
||||||
|
service['repository'].findOne = findOne;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.findOneByConditions({ projectId: 'uuid' }),
|
||||||
|
).resolves.toEqual(entity);
|
||||||
|
|
||||||
|
expect(findOne.mock.calls[0][0]).toMatchObject({
|
||||||
|
projectId: 'uuid',
|
||||||
|
pipelineId: IsNull(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
39
src/configurations/configurations.service.ts
Normal file
39
src/configurations/configurations.service.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { GetConfigurationArgs } from './dto/get-configuration.args';
|
||||||
|
import { BaseDbService } from './../commons/services/base-db.service';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Configuration } from './entities/configuration.entity';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { FindConditions, IsNull, Repository } from 'typeorm';
|
||||||
|
import { SetConfigurationInput } from './dto/set-configuration.input';
|
||||||
|
import { pick } from 'ramda';
|
||||||
|
import { Etcd3 } from 'etcd3';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ConfigurationsService extends BaseDbService<Configuration> {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Configuration)
|
||||||
|
configurationRepository: Repository<Configuration>,
|
||||||
|
private readonly etcd: Etcd3,
|
||||||
|
) {
|
||||||
|
super(configurationRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setConfiguration(dto: SetConfigurationInput) {
|
||||||
|
let entity = await this.repository.findOne(
|
||||||
|
pick(['pipelineId', 'projectId'], dto),
|
||||||
|
);
|
||||||
|
if (!entity) {
|
||||||
|
entity = this.repository.create(dto);
|
||||||
|
}
|
||||||
|
entity = await this.repository.save(entity);
|
||||||
|
this.etcd.put(`share/config/${entity.id}`).value(entity.content);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOneByConditions(dto: FindConditions<GetConfigurationArgs>) {
|
||||||
|
if (dto.projectId && !dto.pipelineId) {
|
||||||
|
dto.pipelineId = IsNull();
|
||||||
|
}
|
||||||
|
return await this.repository.findOne(dto);
|
||||||
|
}
|
||||||
|
}
|
17
src/configurations/dto/get-configuration.args.ts
Normal file
17
src/configurations/dto/get-configuration.args.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { ArgsType } from '@nestjs/graphql';
|
||||||
|
import { IsUUID, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
|
@ArgsType()
|
||||||
|
export class GetConfigurationArgs {
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
pipelineId?: string;
|
||||||
|
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
projectId?: string;
|
||||||
|
|
||||||
|
@IsUUID()
|
||||||
|
@IsOptional()
|
||||||
|
id?: string;
|
||||||
|
}
|
26
src/configurations/dto/set-configuration.input.ts
Normal file
26
src/configurations/dto/set-configuration.input.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { ConfigurationLanguage } from './../enums/configuration-language.enum';
|
||||||
|
import { IsEnum, IsString, IsUUID, Length, IsOptional } from 'class-validator';
|
||||||
|
import { InputType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class SetConfigurationInput {
|
||||||
|
@IsOptional()
|
||||||
|
@IsUUID()
|
||||||
|
id?: string;
|
||||||
|
|
||||||
|
@IsUUID()
|
||||||
|
pipelineId: string;
|
||||||
|
|
||||||
|
@IsUUID()
|
||||||
|
projectId: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@IsEnum(ConfigurationLanguage)
|
||||||
|
language: ConfigurationLanguage;
|
||||||
|
|
||||||
|
@Length(0, 100)
|
||||||
|
@IsOptional()
|
||||||
|
name = 'Default Configuration';
|
||||||
|
}
|
35
src/configurations/entities/configuration.entity.ts
Normal file
35
src/configurations/entities/configuration.entity.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Project } from './../../projects/project.entity';
|
||||||
|
import { ConfigurationLanguage } from './../enums/configuration-language.enum';
|
||||||
|
import { Pipeline } from './../../pipelines/pipeline.entity';
|
||||||
|
import { AppBaseEntity } from './../../commons/entities/app-base-entity';
|
||||||
|
import { ObjectType } from '@nestjs/graphql';
|
||||||
|
import { Column, Entity, ManyToOne } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
@ObjectType()
|
||||||
|
export class Configuration extends AppBaseEntity {
|
||||||
|
@ManyToOne(() => Pipeline)
|
||||||
|
pipeline: Pipeline;
|
||||||
|
|
||||||
|
@Column({ unique: true, nullable: true })
|
||||||
|
pipelineId: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => Project)
|
||||||
|
project: Project;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
projectId: string;
|
||||||
|
|
||||||
|
@Column({ comment: 'language defined in type field.' })
|
||||||
|
content: string;
|
||||||
|
|
||||||
|
@Column({ comment: '配置名称' })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'enum',
|
||||||
|
enum: ConfigurationLanguage,
|
||||||
|
comment: 'configuration content language',
|
||||||
|
})
|
||||||
|
language: ConfigurationLanguage;
|
||||||
|
}
|
10
src/configurations/enums/configuration-language.enum.ts
Normal file
10
src/configurations/enums/configuration-language.enum.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { registerEnumType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
export enum ConfigurationLanguage {
|
||||||
|
JavaScript = 'JavaScript',
|
||||||
|
YAML = 'YAML',
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEnumType(ConfigurationLanguage, {
|
||||||
|
name: 'ConfigurationLanguage',
|
||||||
|
});
|
@ -16,12 +16,10 @@ import {
|
|||||||
} from './pipeline-tasks.constants';
|
} from './pipeline-tasks.constants';
|
||||||
import { PipelineTaskLogger } from './pipeline-task.logger';
|
import { PipelineTaskLogger } from './pipeline-task.logger';
|
||||||
import { PipelineTaskFlushService } from './pipeline-task-flush.service';
|
import { PipelineTaskFlushService } from './pipeline-task-flush.service';
|
||||||
import { CommonsModule } from '../commons/commons.module';
|
|
||||||
import { DeployByPm2Service } from './runners/deploy-by-pm2/deploy-by-pm2.service';
|
import { DeployByPm2Service } from './runners/deploy-by-pm2/deploy-by-pm2.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
CommonsModule,
|
|
||||||
TypeOrmModule.forFeature([PipelineTask, Pipeline]),
|
TypeOrmModule.forFeature([PipelineTask, Pipeline]),
|
||||||
RedisModule,
|
RedisModule,
|
||||||
ReposModule,
|
ReposModule,
|
||||||
|
@ -5,14 +5,11 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||||||
import { Pipeline } from './pipeline.entity';
|
import { Pipeline } from './pipeline.entity';
|
||||||
import { CommitLogsResolver } from './commit-logs.resolver';
|
import { CommitLogsResolver } from './commit-logs.resolver';
|
||||||
import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module';
|
import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module';
|
||||||
import { ReposModule } from '../repos/repos.module';
|
|
||||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { CommonsModule } from '../commons/commons.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
CommonsModule,
|
|
||||||
TypeOrmModule.forFeature([Pipeline]),
|
TypeOrmModule.forFeature([Pipeline]),
|
||||||
PipelineTasksModule,
|
PipelineTasksModule,
|
||||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||||
|
@ -6,11 +6,9 @@ import { Project } from './project.entity';
|
|||||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { EXCHANGE_PROJECT_FANOUT } from './projects.constants';
|
import { EXCHANGE_PROJECT_FANOUT } from './projects.constants';
|
||||||
import { CommonsModule } from '../commons/commons.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
CommonsModule,
|
|
||||||
TypeOrmModule.forFeature([Project]),
|
TypeOrmModule.forFeature([Project]),
|
||||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
|
@ -7,14 +7,12 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
|||||||
import { ProjectsModule } from '../projects/projects.module';
|
import { ProjectsModule } from '../projects/projects.module';
|
||||||
import { EXCHANGE_REPO } from './repos.constants';
|
import { EXCHANGE_REPO } from './repos.constants';
|
||||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||||
import { CommonsModule } from '../commons/commons.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([Project]),
|
TypeOrmModule.forFeature([Project]),
|
||||||
ConfigModule,
|
ConfigModule,
|
||||||
ProjectsModule,
|
ProjectsModule,
|
||||||
CommonsModule,
|
|
||||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||||
imports: [ConfigModule],
|
imports: [ConfigModule],
|
||||||
useFactory: (configService: ConfigService) => ({
|
useFactory: (configService: ConfigService) => ({
|
||||||
|
Loading…
Reference in New Issue
Block a user