10 Commits

39 changed files with 1818 additions and 308 deletions

4
.gitignore vendored
View File

@@ -33,4 +33,6 @@ lerna-debug.log*
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
/config.yml /config.yml
tsconfig.build.tsbuildinfo
.eslintcache

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

5
.husky/pre-commit Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
npm test

1740
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,8 @@
"test:watch": "jest --watch", "test:watch": "jest --watch",
"test:cov": "jest --coverage", "test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json",
"prepare": "husky install"
}, },
"dependencies": { "dependencies": {
"@golevelup/nestjs-rabbitmq": "^1.16.1", "@golevelup/nestjs-rabbitmq": "^1.16.1",
@@ -36,6 +37,7 @@
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"class-transformer": "^0.3.2", "class-transformer": "^0.3.2",
"class-validator": "^0.13.1", "class-validator": "^0.13.1",
"configuration": "file:../configuration",
"debug": "^4.3.1", "debug": "^4.3.1",
"graphql": "^15.5.0", "graphql": "^15.5.0",
"graphql-tools": "^8.1.0", "graphql-tools": "^8.1.0",
@@ -74,7 +76,9 @@
"eslint": "^7.12.1", "eslint": "^7.12.1",
"eslint-config-prettier": "7.2.0", "eslint-config-prettier": "7.2.0",
"eslint-plugin-prettier": "^3.1.4", "eslint-plugin-prettier": "^3.1.4",
"husky": "^7.0.2",
"jest": "^26.6.3", "jest": "^26.6.3",
"lint-staged": "^11.1.2",
"prettier": "^2.1.2", "prettier": "^2.1.2",
"supertest": "^6.0.0", "supertest": "^6.0.0",
"ts-jest": "^26.4.3", "ts-jest": "^26.4.3",
@@ -102,5 +106,8 @@
}, },
"coverageDirectory": "../coverage", "coverageDirectory": "../coverage",
"testEnvironment": "node" "testEnvironment": "node"
},
"lint-staged": {
"{src,apps,libs,test}/**/*.ts": "eslint --cache --fix"
} }
} }

View File

@@ -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: [
@@ -64,7 +65,7 @@ import { fromPairs, map, pipe, toPairs } from 'ramda';
playground: true, playground: true,
autoSchemaFile: true, autoSchemaFile: true,
installSubscriptionHandlers: true, installSubscriptionHandlers: true,
context: ({ req, connection, ...args }) => { context: ({ req, connection }) => {
return connection ? { req: connection.context } : { req }; return connection ? { req: connection.context } : { req };
}, },
subscriptions: { subscriptions: {
@@ -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],

View File

@@ -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],

View File

@@ -13,9 +13,9 @@ export class ApplicationException extends Error {
this.error = message.error; this.error = message.error;
this.message = message.message as any; this.message = message.message as any;
} else if (typeof message === 'string') { } else if (typeof message === 'string') {
super((message as unknown) as any); super(message as unknown as any);
} else { } else {
super((message as unknown) as any); super(message as unknown as any);
} }
} }

View File

@@ -19,12 +19,16 @@ export class HttpExceptionFilter implements ExceptionFilter {
case 'graphql': { case 'graphql': {
const errorName = exception.message; const errorName = exception.message;
const extensions: Record<string, any> = {}; const extensions: Record<string, any> = {};
const err = exception.getResponse(); const err = exception.getResponse() as any;
if (typeof err === 'string') { if (typeof err === 'string') {
extensions.message = err; extensions.message = err;
} else { } else {
Object.assign(extensions, (err as any).extension); Object.assign(extensions, err.extension);
extensions.message = (err as any).message; if (typeof err.message === 'string') {
extensions.message = err.message;
} else {
extensions.message = err.error;
}
} }
extensions.error = errorName; extensions.error = errorName;
this.logger.error(extensions); this.logger.error(extensions);

View File

@@ -126,6 +126,7 @@ export class BaseDbService<Entity extends AppBaseEntity> extends TypeormHelper {
} }
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async canYouRemoveWithIds(ids: string[]): Promise<void> { async canYouRemoveWithIds(ids: string[]): Promise<void> {
return; return;
} }

View 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 {}

View File

@@ -0,0 +1,30 @@
import { JwtService } from '@nestjs-lib/auth';
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: [
ConfigurationsResolver,
{
provide: ConfigurationsService,
useValue: {},
},
{
provide: JwtService,
useValue: {},
},
],
}).compile();
resolver = module.get<ConfigurationsResolver>(ConfigurationsResolver);
});
it('should be defined', () => {
expect(resolver).toBeDefined();
});
});

View 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);
}
}

View File

@@ -0,0 +1,49 @@
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';
import { Etcd3 } from 'etcd3';
describe('ConfigurationsService', () => {
let service: ConfigurationsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ConfigurationsService,
{
provide: getRepositoryToken(Configuration),
useValue: {},
},
{
provide: Etcd3,
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<any, [any]>(() => 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(),
});
});
});
});

View File

@@ -0,0 +1,55 @@
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);
await this.syncToEtcd(entity);
return entity;
}
async findOneByConditions(dto: FindConditions<GetConfigurationArgs>) {
if (dto.projectId && !dto.pipelineId) {
dto.pipelineId = IsNull();
}
return await this.repository.findOne(dto);
}
async syncToEtcd({ pipelineId, id }: { pipelineId?: string; id?: string }) {
const config = await this.repository.findOneOrFail({
where: { pipelineId, id },
relations: ['pipeline', 'project'],
});
await this.etcd
.put(`share/config/${config.id}`)
.value(config.content)
.exec();
await this.etcd
.put(`share/config/${config.pipeline.environment}/${config.project.name}`)
.value(config.content)
.exec();
}
}

View 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;
}

View 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';
}

View 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;
}

View File

@@ -0,0 +1,10 @@
import { registerEnumType } from '@nestjs/graphql';
export enum ConfigurationLanguage {
JavaScript = 'JavaScript',
YAML = 'YAML',
}
registerEnumType(ConfigurationLanguage, {
name: 'ConfigurationLanguage',
});

View File

@@ -5,6 +5,7 @@ import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { HttpExceptionFilter } from './commons/filters/all.exception-filter'; import { HttpExceptionFilter } from './commons/filters/all.exception-filter';
import { SanitizePipe } from './commons/pipes/sanitize.pipe'; import { SanitizePipe } from './commons/pipes/sanitize.pipe';
import { ServiceRegister } from 'configuration';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule, { bodyParser: false }); const app = await NestFactory.create(AppModule, { bodyParser: false });
@@ -17,6 +18,10 @@ async function bootstrap() {
); );
const httpExceptionFilterLogger = await app.resolve(PinoLogger); const httpExceptionFilterLogger = await app.resolve(PinoLogger);
app.useGlobalFilters(new HttpExceptionFilter(httpExceptionFilterLogger)); app.useGlobalFilters(new HttpExceptionFilter(httpExceptionFilterLogger));
await app.listen(configService.get<number>('http.port')); const server = await app.listen(configService.get<number>('http.port', 0));
const port = server.address().port;
const register = new ServiceRegister({ etcd: { hosts: 'http://rpi:2379' } });
register.register('fennec/api', `http://localhost:${port}`);
register.register('api.fennec', `http://localhost:${port}`);
} }
bootstrap(); bootstrap();

View File

@@ -1,4 +1,4 @@
import { Field, InputType } from '@nestjs/graphql'; import { InputType } from '@nestjs/graphql';
import { PipelineUnits } from '../enums/pipeline-units.enum'; import { PipelineUnits } from '../enums/pipeline-units.enum';
@InputType() @InputType()

View File

@@ -1,6 +1,6 @@
import { Field, InputType, Int, ObjectType } from '@nestjs/graphql'; import { Field, InputType, Int, ObjectType } from '@nestjs/graphql';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { IsInstance, isInstance, ValidateNested } from 'class-validator'; import { IsInstance, ValidateNested } from 'class-validator';
import { WorkUnit } from './work-unit.model'; import { WorkUnit } from './work-unit.model';
@InputType('WorkUnitMetadataInput') @InputType('WorkUnitMetadataInput')

View File

@@ -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,

View File

@@ -35,15 +35,6 @@ export class PipelineTasksResolver {
); );
} }
@Subscription(() => PipelineTask, {
resolve: (value) => {
return value;
},
})
async pipelineTaskChanged(@Args('id') id: string) {
// return await this.service.watchTaskUpdated(id);
}
@Query(() => [PipelineTask]) @Query(() => [PipelineTask])
async listPipelineTaskByPipelineId(@Args('pipelineId') pipelineId: string) { async listPipelineTaskByPipelineId(@Args('pipelineId') pipelineId: string) {
return await this.service.listTasksByPipelineId(pipelineId); return await this.service.listTasksByPipelineId(pipelineId);

View File

@@ -12,7 +12,6 @@ describe('PipelineTasksService', () => {
let service: PipelineTasksService; let service: PipelineTasksService;
let module: TestingModule; let module: TestingModule;
let taskRepository: Repository<PipelineTask>; let taskRepository: Repository<PipelineTask>;
let pipelineRepository: Repository<Pipeline>;
beforeEach(async () => { beforeEach(async () => {
module = await Test.createTestingModule({ module = await Test.createTestingModule({
@@ -43,7 +42,6 @@ describe('PipelineTasksService', () => {
service = module.get<PipelineTasksService>(PipelineTasksService); service = module.get<PipelineTasksService>(PipelineTasksService);
taskRepository = module.get(getRepositoryToken(PipelineTask)); taskRepository = module.get(getRepositoryToken(PipelineTask));
pipelineRepository = module.get(getRepositoryToken(Pipeline));
jest jest
.spyOn(taskRepository, 'save') .spyOn(taskRepository, 'save')
.mockImplementation(async (data: any) => data); .mockImplementation(async (data: any) => data);

View File

@@ -4,7 +4,6 @@ import { PipelineTask } from './pipeline-task.entity';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { CreatePipelineTaskInput } from './dtos/create-pipeline-task.input'; import { CreatePipelineTaskInput } from './dtos/create-pipeline-task.input';
import { Pipeline } from '../pipelines/pipeline.entity'; import { Pipeline } from '../pipelines/pipeline.entity';
import debug from 'debug';
import { AmqpConnection, RabbitRPC } from '@golevelup/nestjs-rabbitmq'; import { AmqpConnection, RabbitRPC } from '@golevelup/nestjs-rabbitmq';
import { import {
EXCHANGE_PIPELINE_TASK_TOPIC, EXCHANGE_PIPELINE_TASK_TOPIC,
@@ -19,8 +18,6 @@ import { InjectPinoLogger, PinoLogger } from 'nestjs-pino';
import { getAppInstanceRouteKey } from '../commons/utils/rabbit-mq'; import { getAppInstanceRouteKey } from '../commons/utils/rabbit-mq';
import { ROUTE_PIPELINE_TASK_KILL } from './pipeline-tasks.constants'; import { ROUTE_PIPELINE_TASK_KILL } from './pipeline-tasks.constants';
const log = debug('fennec:pipeline-tasks:service');
@Injectable() @Injectable()
export class PipelineTasksService { export class PipelineTasksService {
constructor( constructor(

View File

@@ -28,4 +28,8 @@ export class CreatePipelineInput {
@ValidateNested() @ValidateNested()
@IsInstance(WorkUnitMetadata) @IsInstance(WorkUnitMetadata)
workUnitMetadata: WorkUnitMetadata; workUnitMetadata: WorkUnitMetadata;
@IsString()
@MaxLength(100)
environment: string;
} }

View File

@@ -12,7 +12,7 @@ export class Pipeline extends AppBaseEntity {
@Column() @Column()
projectId: string; projectId: string;
@Column({ comment: 'eg: remotes/origin/master' }) @Column({ comment: 'E.g., remotes/origin/master' })
branch: string; branch: string;
@Column() @Column()
@@ -20,4 +20,7 @@ export class Pipeline extends AppBaseEntity {
@Column({ type: 'jsonb' }) @Column({ type: 'jsonb' })
workUnitMetadata: WorkUnitMetadata; workUnitMetadata: WorkUnitMetadata;
@Column()
environment: string;
} }

View File

@@ -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, {

View File

@@ -2,13 +2,11 @@ import { Test, TestingModule } from '@nestjs/testing';
import { PipelinesService } from './pipelines.service'; import { PipelinesService } from './pipelines.service';
import { Pipeline } from './pipeline.entity'; import { Pipeline } from './pipeline.entity';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Project } from '../projects/project.entity'; import { Project } from '../projects/project.entity';
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
describe('PipelinesService', () => { describe('PipelinesService', () => {
let service: PipelinesService; let service: PipelinesService;
let repository: Repository<Pipeline>;
let pipeline: Pipeline; let pipeline: Pipeline;
beforeEach(async () => { beforeEach(async () => {
@@ -40,7 +38,6 @@ describe('PipelinesService', () => {
}).compile(); }).compile();
service = module.get<PipelinesService>(PipelinesService); service = module.get<PipelinesService>(PipelinesService);
repository = module.get(getRepositoryToken(Pipeline));
}); });
it('should be defined', () => { it('should be defined', () => {

View File

@@ -19,7 +19,9 @@ import { plainToClass } from 'class-transformer';
@Injectable() @Injectable()
export class PipelinesService extends BaseDbService<Pipeline> { export class PipelinesService extends BaseDbService<Pipeline> {
readonly uniqueFields: Array<Array<keyof Pipeline>> = [['projectId', 'name']]; readonly uniqueFields: Array<Array<keyof Pipeline>> = [
['projectId', 'name', 'environment'],
];
constructor( constructor(
@InjectRepository(Pipeline) @InjectRepository(Pipeline)
readonly repository: Repository<Pipeline>, readonly repository: Repository<Pipeline>,

View File

@@ -1,5 +1,5 @@
import { ObjectType } from '@nestjs/graphql'; import { ObjectType } from '@nestjs/graphql';
import { Entity, Column, DeleteDateColumn } from 'typeorm'; import { Entity, Column } from 'typeorm';
import { AppBaseEntity } from '../commons/entities/app-base-entity'; import { AppBaseEntity } from '../commons/entities/app-base-entity';
@ObjectType() @ObjectType()

View File

@@ -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],

View File

@@ -1,10 +1,5 @@
import { ObjectType, Field } from '@nestjs/graphql'; import { ObjectType, Field } from '@nestjs/graphql';
import { import { BranchSummaryBranch } from 'simple-git';
LogResult,
DefaultLogFields,
BranchSummary,
BranchSummaryBranch,
} from 'simple-git';
@ObjectType() @ObjectType()
export class Branch implements BranchSummaryBranch { export class Branch implements BranchSummaryBranch {

View File

@@ -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) => ({

View File

@@ -161,7 +161,7 @@ describe('ReposService', () => {
const project = new Project(); const project = new Project();
const pipeline = new Pipeline(); const pipeline = new Pipeline();
pipeline.branch = 'test'; pipeline.branch = 'test';
const fetch = jest.fn((_: any) => Promise.resolve()); const fetch = jest.fn<any, [any]>(() => Promise.resolve());
pipeline.project = project; pipeline.project = project;
const getGit = jest.spyOn(service, 'getGit').mockImplementation(() => const getGit = jest.spyOn(service, 'getGit').mockImplementation(() =>
Promise.resolve({ Promise.resolve({
@@ -182,7 +182,7 @@ describe('ReposService', () => {
const project = new Project(); const project = new Project();
const pipeline = new Pipeline(); const pipeline = new Pipeline();
pipeline.branch = 'test'; pipeline.branch = 'test';
const fetch = jest.fn((_: any) => Promise.resolve()); const fetch = jest.fn<any, [any]>(() => Promise.resolve());
pipeline.project = project; pipeline.project = project;
const getGit = jest const getGit = jest
.spyOn(service, 'getGit') .spyOn(service, 'getGit')
@@ -196,7 +196,7 @@ describe('ReposService', () => {
const project = new Project(); const project = new Project();
const pipeline = new Pipeline(); const pipeline = new Pipeline();
pipeline.branch = 'test'; pipeline.branch = 'test';
const fetch = jest.fn((_: any) => Promise.reject('error')); const fetch = jest.fn<any, [any]>(() => Promise.reject('error'));
pipeline.project = project; pipeline.project = project;
const getGit = jest.spyOn(service, 'getGit').mockImplementation(() => const getGit = jest.spyOn(service, 'getGit').mockImplementation(() =>
Promise.resolve({ Promise.resolve({

View File

@@ -1,4 +1,4 @@
import { MiddlewareConsumer, Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module'; import { PipelineTasksModule } from '../pipeline-tasks/pipeline-tasks.module';
import { GiteaWebhooksController } from './gitea-webhooks.controller'; import { GiteaWebhooksController } from './gitea-webhooks.controller';
@@ -10,5 +10,4 @@ import { WebhooksService } from './webhooks.service';
controllers: [GiteaWebhooksController], controllers: [GiteaWebhooksController],
providers: [WebhooksService], providers: [WebhooksService],
}) })
export class WebhooksModule { export class WebhooksModule {}
}

File diff suppressed because one or more lines are too long

View File

@@ -10,8 +10,6 @@
"lib": ["ES2021"], "lib": ["ES2021"],
"sourceMap": true, "sourceMap": true,
"outDir": "./dist", "outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./", "baseUrl": "./",
"incremental": true,
}, },
} }