Compare commits
No commits in common. "develop" and "master" have entirely different histories.
25
.eslintrc.js
25
.eslintrc.js
@ -1,25 +0,0 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier/@typescript-eslint',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
36
.gitignore
vendored
36
.gitignore
vendored
@ -1,36 +0,0 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
/config.yml
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
16
.vscode/settings.json
vendored
16
.vscode/settings.json
vendored
@ -1,16 +0,0 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"boardcat",
|
||||
"execa",
|
||||
"gitea",
|
||||
"lpush",
|
||||
"lrange",
|
||||
"metatype",
|
||||
"pmessage",
|
||||
"psubscribe",
|
||||
"QLJSON",
|
||||
"Repos",
|
||||
"rpop",
|
||||
"rpush"
|
||||
]
|
||||
}
|
@ -14,6 +14,5 @@ db:
|
||||
port: 6379
|
||||
password:
|
||||
prefix: blog
|
||||
etcd:
|
||||
hosts:
|
||||
- 'http://192.168.31.194:2379'
|
||||
workspaces:
|
||||
root: '/Users/ivanli/Projects/fennec/workspaces'
|
@ -1,20 +0,0 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: 'postgres:14'
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
ports:
|
||||
- '${PG_PORT}:5432'
|
||||
redis:
|
||||
image: 'redis:6'
|
||||
restart: always
|
||||
ports:
|
||||
- '${REDIS_PORT}:6379'
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: 'blog-dev'
|
||||
driver: bridge
|
@ -1,61 +0,0 @@
|
||||
import execa from 'execa';
|
||||
import { URL } from 'url';
|
||||
import { findFreePorts } from 'find-free-ports';
|
||||
import YAML from 'js-yaml';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
|
||||
const [PG_PORT, REDIS_PORT] = await findFreePorts(2);
|
||||
|
||||
await execa(
|
||||
'docker-compose',
|
||||
[
|
||||
'-f',
|
||||
new URL('./docker-compose.dev.yml', import.meta.url).pathname,
|
||||
'up',
|
||||
'-d',
|
||||
],
|
||||
{
|
||||
env: {
|
||||
PG_PORT,
|
||||
REDIS_PORT,
|
||||
},
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
},
|
||||
);
|
||||
|
||||
console.log(`✅ Postgres is running on port ${PG_PORT}`);
|
||||
console.log(`✅ Redis is running on port ${REDIS_PORT}`);
|
||||
|
||||
const config = await YAML.load(
|
||||
await readFile(
|
||||
new URL('../config.yml.example', import.meta.url).pathname,
|
||||
'utf-8',
|
||||
),
|
||||
);
|
||||
config.db.postgres = {
|
||||
host: 'localhost',
|
||||
port: PG_PORT,
|
||||
database: 'postgres',
|
||||
username: 'postgres',
|
||||
password: '',
|
||||
};
|
||||
|
||||
config.db.redis = {
|
||||
host: 'localhost',
|
||||
port: REDIS_PORT,
|
||||
};
|
||||
|
||||
const configOutputPath = new URL('../config.yml', import.meta.url).pathname;
|
||||
|
||||
await writeFile(configOutputPath, YAML.dump(config), 'utf-8');
|
||||
|
||||
console.log(`✅ Config file is written to ${configOutputPath}`);
|
||||
|
||||
await execa.command('npm run typeorm -- migration:run', {
|
||||
cwd: new URL('../', import.meta.url).pathname,
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
});
|
||||
|
||||
console.log(`✅ Database Initiated!`);
|
@ -1,14 +0,0 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'blog-be',
|
||||
script: 'npm',
|
||||
args: 'run start:prod',
|
||||
watch: false,
|
||||
ignore_watch: ['node_modules'],
|
||||
log_date_format: 'MM-DD HH:mm:ss.SSS Z',
|
||||
env: {},
|
||||
max_restarts: 5,
|
||||
},
|
||||
],
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class articleAndTagMigration1635661602570 implements MigrationInterface {
|
||||
name = 'articleAndTagMigration1635661602570'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE "article" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP, "title" character varying NOT NULL, "content" text NOT NULL, "publishedAt" TIMESTAMP, "tags" character varying array NOT NULL, CONSTRAINT "PK_40808690eb7b915046558c0f81b" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_f330baf6be412e8dd60ff7f78e" ON "article" ("publishedAt") `);
|
||||
await queryRunner.query(`CREATE TABLE "tag" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP, "name" character varying(100) NOT NULL, CONSTRAINT "PK_8e4052373c579afc1471f526760" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_6a9775008add570dc3e5a0bab7" ON "tag" ("name") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_6a9775008add570dc3e5a0bab7"`);
|
||||
await queryRunner.query(`DROP TABLE "tag"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_f330baf6be412e8dd60ff7f78e"`);
|
||||
await queryRunner.query(`DROP TABLE "article"`);
|
||||
}
|
||||
|
||||
}
|
22
ormconfig.js
22
ormconfig.js
@ -1,22 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const yaml = require('js-yaml');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const config = yaml.load(
|
||||
fs.readFileSync(path.join(__dirname, './config.yml'), 'utf8'),
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
type: 'postgres',
|
||||
host: config.db.postgres.host,
|
||||
port: config.db.postgres.port,
|
||||
username: config.db.postgres.username,
|
||||
password: config.db.postgres.password,
|
||||
database: config.db.postgres.database,
|
||||
migrations: ['migrations/**/*.ts'],
|
||||
entities: ['src/**/*.entity.ts'],
|
||||
cli: {
|
||||
migrationsDir: 'migrations',
|
||||
},
|
||||
};
|
25330
package-lock.json
generated
25330
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
108
package.json
108
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "blog-be",
|
||||
"version": "0.1.0",
|
||||
"name": "fennec-be",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
@ -13,74 +13,67 @@
|
||||
"start:dev": "DEBUG=fennec:* nest start --watch",
|
||||
"start:debug": "DEBUG=fennec:* nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"init:dev": "node docker/init-dev.mjs",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"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",
|
||||
"typeorm": "node --require ts-node/register ./node_modules/typeorm/cli.js"
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fennec/configuration": "^0.0.2",
|
||||
"@nestjs-lib/auth": "^0.2.3",
|
||||
"@nestjs-lib/etcd3": "^0.0.1",
|
||||
"@nestjs/common": "^8.1.1",
|
||||
"@nestjs/config": "^1.0.3",
|
||||
"@nestjs/core": "^8.1.1",
|
||||
"@nestjs/graphql": "^9.1.1",
|
||||
"@nestjs/platform-express": "^8.1.1",
|
||||
"@nestjs/typeorm": "^8.0.2",
|
||||
"apollo-server-express": "^3.4.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"@nestjs/bull": "^0.3.1",
|
||||
"@nestjs/common": "^7.5.1",
|
||||
"@nestjs/config": "^0.6.2",
|
||||
"@nestjs/core": "^7.5.1",
|
||||
"@nestjs/graphql": "^7.9.8",
|
||||
"@nestjs/platform-express": "^7.5.1",
|
||||
"@nestjs/typeorm": "^7.1.5",
|
||||
"@types/bull": "^3.15.0",
|
||||
"apollo-server-express": "^2.19.2",
|
||||
"bcrypt": "^5.0.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"class-transformer": "^0.4.0",
|
||||
"bull": "^3.20.1",
|
||||
"class-transformer": "^0.3.2",
|
||||
"class-validator": "^0.13.1",
|
||||
"debug": "^4.3.2",
|
||||
"graphql": "^15.6.1",
|
||||
"graphql-tools": "^8.2.0",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
"highlight.js": "^11.3.1",
|
||||
"ioredis": "^4.28.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"marked": "^3.0.7",
|
||||
"nestjs-redis": "^1.3.3",
|
||||
"observable-to-async-generator": "^1.0.2",
|
||||
"pg": "^8.7.1",
|
||||
"debug": "^4.3.1",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-tools": "^7.0.2",
|
||||
"ioredis": "^4.25.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"nestjs-redis": "^1.2.8",
|
||||
"observable-to-async-generator": "^1.0.1-rc",
|
||||
"pg": "^8.5.1",
|
||||
"ramda": "^0.27.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.4.0",
|
||||
"simple-git": "^2.47.0",
|
||||
"typeorm": "^0.2.38"
|
||||
"rxjs": "^6.6.3",
|
||||
"simple-git": "^2.35.0",
|
||||
"typeorm": "^0.2.30"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.1.4",
|
||||
"@nestjs/schematics": "^8.0.4",
|
||||
"@nestjs/testing": "^8.1.1",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/highlight.js": "^10.1.0",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/marked": "^3.0.2",
|
||||
"@types/node": "^16.11.2",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.1.0",
|
||||
"@typescript-eslint/parser": "^5.1.0",
|
||||
"apollo-server-testing": "^2.23.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"execa": "^5.1.1",
|
||||
"find-free-ports": "^3.0.0",
|
||||
"jest": "^27.3.1",
|
||||
"prettier": "^2.4.1",
|
||||
"supertest": "^6.1.6",
|
||||
"ts-jest": "^27.0.7",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^10.3.0",
|
||||
"tsconfig-paths": "^3.11.0",
|
||||
"typescript": "^4.4.4"
|
||||
"@nestjs/cli": "^7.5.7",
|
||||
"@nestjs/schematics": "^7.1.3",
|
||||
"@nestjs/testing": "^7.5.1",
|
||||
"@types/body-parser": "^1.19.0",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/express": "^4.17.8",
|
||||
"@types/ioredis": "^4.22.3",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^14.14.6",
|
||||
"@types/supertest": "^2.0.10",
|
||||
"@typescript-eslint/eslint-plugin": "^4.6.1",
|
||||
"@typescript-eslint/parser": "^4.6.1",
|
||||
"eslint": "^7.12.1",
|
||||
"eslint-config-prettier": "7.2.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"jest": "^26.6.3",
|
||||
"prettier": "^2.1.2",
|
||||
"supertest": "^6.0.0",
|
||||
"ts-jest": "^26.4.3",
|
||||
"ts-loader": "^8.0.8",
|
||||
"ts-node": "^9.0.0",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"typescript": "^4.0.5"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
@ -96,9 +89,6 @@
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^jose/(.*)$": "<rootDir>/../node_modules/jose/dist/node/cjs/$1"
|
||||
},
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
|
@ -6,19 +6,18 @@ import { AppController } from './app.controller';
|
||||
import { AppResolver } from './app.resolver';
|
||||
import { AppService } from './app.service';
|
||||
import configuration from './commons/config/configuration';
|
||||
import { RedisModule } from 'nestjs-redis';
|
||||
import { ParseBodyMiddleware } from './commons/middleware/parse-body.middleware';
|
||||
import { ArticlesModule } from './articles/articles.module';
|
||||
import { EtcdModule } from '@nestjs-lib/etcd3';
|
||||
import { CommonsModule } from './commons/commons.module';
|
||||
import { TagsModule } from './tags/tags.module';
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { PubSubModule } from './commons/pub-sub/pub-sub.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
load: [configuration],
|
||||
isGlobal: true,
|
||||
}),
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
type: 'postgres',
|
||||
host: configService.get<string>('db.postgres.host'),
|
||||
@ -26,12 +25,13 @@ import { TagsModule } from './tags/tags.module';
|
||||
username: configService.get<string>('db.postgres.username'),
|
||||
password: configService.get<string>('db.postgres.password'),
|
||||
database: configService.get<string>('db.postgres.database'),
|
||||
synchronize: false,
|
||||
synchronize: true,
|
||||
autoLoadEntities: true,
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
GraphQLModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
debug: configService.get<string>('env') !== 'prod',
|
||||
playground: true,
|
||||
@ -40,15 +40,38 @@ import { TagsModule } from './tags/tags.module';
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
EtcdModule.forRootAsync({
|
||||
BullModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
hosts: configService.get<string>('db.etcd.hosts', 'localhost:2379'),
|
||||
redis: {
|
||||
host: configService.get<string>('db.redis.host', 'localhost'),
|
||||
port: configService.get<number>('db.redis.port', undefined),
|
||||
password: configService.get<string>('db.redis.password', undefined),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
PubSubModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
redis: {
|
||||
host: configService.get<string>('db.redis.host', 'localhost'),
|
||||
port: configService.get<number>('db.redis.port', undefined),
|
||||
password: configService.get<string>('db.redis.password', undefined),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
RedisModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
host: configService.get<string>('db.redis.host', 'localhost'),
|
||||
port: configService.get<number>('db.redis.port', 6379),
|
||||
password: configService.get<string>('db.redis.password', ''),
|
||||
keyPrefix: configService.get<string>('db.redis.prefix', 'blog') + ':',
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
CommonsModule,
|
||||
ArticlesModule,
|
||||
TagsModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService, AppResolver],
|
||||
|
@ -1,12 +0,0 @@
|
||||
import { CommonsModule } from './../commons/commons.module';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ArticlesService } from './articles.service';
|
||||
import { ArticlesResolver } from './articles.resolver';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Article } from './entities/article.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Article]), CommonsModule],
|
||||
providers: [ArticlesResolver, ArticlesService],
|
||||
})
|
||||
export class ArticlesModule {}
|
@ -1,30 +0,0 @@
|
||||
import { JwtService } from '@nestjs-lib/auth';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ArticlesResolver } from './articles.resolver';
|
||||
import { ArticlesService } from './articles.service';
|
||||
|
||||
describe('ArticlesResolver', () => {
|
||||
let resolver: ArticlesResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ArticlesResolver,
|
||||
{
|
||||
provide: ArticlesService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: JwtService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<ArticlesResolver>(ArticlesResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
@ -1,90 +0,0 @@
|
||||
import {
|
||||
Resolver,
|
||||
Query,
|
||||
Mutation,
|
||||
Args,
|
||||
Int,
|
||||
ResolveField,
|
||||
Parent,
|
||||
} from '@nestjs/graphql';
|
||||
import { ArticlesService } from './articles.service';
|
||||
import { Article } from './entities/article.entity';
|
||||
import { CreateArticleInput } from './dto/create-article.input';
|
||||
import { UpdateArticleInput } from './dto/update-article.input';
|
||||
import * as marked from 'marked';
|
||||
import highlight from 'highlight.js';
|
||||
import { AccountRole, Roles } from '@nestjs-lib/auth';
|
||||
import { ArticleHistory } from './models/article-history.model';
|
||||
|
||||
@Resolver(() => Article)
|
||||
export class ArticlesResolver {
|
||||
constructor(private readonly articlesService: ArticlesService) {}
|
||||
|
||||
@Roles(AccountRole.admin, AccountRole.super)
|
||||
@Mutation(() => Article)
|
||||
createArticle(
|
||||
@Args('createArticleInput') createArticleInput: CreateArticleInput,
|
||||
) {
|
||||
return this.articlesService.create(createArticleInput);
|
||||
}
|
||||
|
||||
@Query(() => [Article], { name: 'articles' })
|
||||
async findAll() {
|
||||
return await this.articlesService.findAll();
|
||||
}
|
||||
|
||||
@Query(() => Article, { name: 'article' })
|
||||
findOne(@Args('id', { type: () => String }) id: string) {
|
||||
return this.articlesService.findOne(id);
|
||||
}
|
||||
|
||||
@Roles(AccountRole.admin, AccountRole.super)
|
||||
@Mutation(() => Article)
|
||||
async updateArticle(
|
||||
@Args('updateArticleInput') updateArticleInput: UpdateArticleInput,
|
||||
) {
|
||||
const article = await this.articlesService.findOne(updateArticleInput.id);
|
||||
return this.articlesService.update(article, updateArticleInput);
|
||||
}
|
||||
|
||||
@Roles(AccountRole.admin, AccountRole.super)
|
||||
@Mutation(() => Int)
|
||||
removeArticle(@Args('id', { type: () => String }) id: string) {
|
||||
return this.articlesService.remove(id);
|
||||
}
|
||||
|
||||
@ResolveField(() => String)
|
||||
async html(@Parent() article: Article) {
|
||||
const tokens = marked.lexer(article.content);
|
||||
const index = tokens.findIndex((token) => ['heading'].includes(token.type));
|
||||
if (index !== -1) {
|
||||
tokens.splice(index, 1);
|
||||
}
|
||||
return marked.parser(tokens, {
|
||||
gfm: true,
|
||||
smartLists: true,
|
||||
smartypants: true,
|
||||
langPrefix: 'hljs language-',
|
||||
highlight: (code, language) => {
|
||||
return highlight.highlight(code, {
|
||||
language: highlight.getLanguage(language) ? language : 'plaintext',
|
||||
}).value;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ResolveField(() => String, { nullable: true })
|
||||
async description(@Parent() article: Article) {
|
||||
const tokens = marked.lexer(article.content);
|
||||
const token = tokens.find((token) =>
|
||||
['blockquote', 'paragraph'].includes(token.type),
|
||||
) as { text: string };
|
||||
|
||||
return token?.text;
|
||||
}
|
||||
|
||||
@ResolveField(() => [ArticleHistory])
|
||||
async histories(@Parent() article: Article) {
|
||||
return article.histories;
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { ArticlesService } from './articles.service';
|
||||
import { Article } from './entities/article.entity';
|
||||
|
||||
describe('ArticlesService', () => {
|
||||
let service: ArticlesService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ArticlesService,
|
||||
{
|
||||
provide: getRepositoryToken(Article),
|
||||
useValue: new Repository(),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ArticlesService>(ArticlesService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseDbService } from '../commons/services/base-db.service';
|
||||
import { CreateArticleInput } from './dto/create-article.input';
|
||||
import { UpdateArticleInput } from './dto/update-article.input';
|
||||
import { Article } from './entities/article.entity';
|
||||
|
||||
@Injectable()
|
||||
export class ArticlesService extends BaseDbService<Article> {
|
||||
readonly uniqueFields: Array<keyof Article> = ['title'];
|
||||
constructor(
|
||||
@InjectRepository(Article)
|
||||
readonly repository: Repository<Article>,
|
||||
) {
|
||||
super(repository);
|
||||
}
|
||||
async create(createArticleInput: CreateArticleInput) {
|
||||
await this.isDuplicateEntity(createArticleInput);
|
||||
return await this.repository.save(
|
||||
this.repository.create(createArticleInput),
|
||||
);
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
return await this.repository.find({
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
||||
async update(article: Article, updateArticleInput: UpdateArticleInput) {
|
||||
await this.isDuplicateEntityForUpdate(article.id, updateArticleInput);
|
||||
return await this.repository.save(
|
||||
this.repository.merge(article, updateArticleInput),
|
||||
);
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
await this.canRemove([id]);
|
||||
return await this.repository.softDelete({ id }).then((d) => d.affected);
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { ObjectType, OmitType } from '@nestjs/graphql';
|
||||
import { Article } from '../entities/article.entity';
|
||||
|
||||
@ObjectType()
|
||||
export class ArticleListItemDto extends OmitType(Article, [
|
||||
'content',
|
||||
] as const) {}
|
@ -1,20 +0,0 @@
|
||||
import { InputType } from '@nestjs/graphql';
|
||||
import { IsDate, IsOptional, IsString, Length } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class CreateArticleInput {
|
||||
@IsString()
|
||||
@Length(1, 100)
|
||||
title: string;
|
||||
|
||||
@IsString()
|
||||
@Length(2, 100000)
|
||||
content: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsDate()
|
||||
publishedAt?: Date;
|
||||
|
||||
@IsString({ each: true })
|
||||
tags: string[];
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { CreateArticleInput } from './create-article.input';
|
||||
import { InputType, Field, PartialType } from '@nestjs/graphql';
|
||||
|
||||
@InputType()
|
||||
export class UpdateArticleInput extends PartialType(CreateArticleInput) {
|
||||
@Field(() => String)
|
||||
id: string;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import { ArticleHistory } from './../models/article-history.model';
|
||||
import { HideField, ObjectType } from '@nestjs/graphql';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
import { AppBaseEntity } from '../../commons/entities/app-base-entity';
|
||||
|
||||
@Entity()
|
||||
@ObjectType()
|
||||
export class Article extends AppBaseEntity {
|
||||
@Column()
|
||||
title: string;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
content: string;
|
||||
|
||||
@Index()
|
||||
@Column({ nullable: true })
|
||||
publishedAt?: Date;
|
||||
|
||||
@Column({ type: 'varchar', array: true })
|
||||
tags: string[];
|
||||
|
||||
@HideField()
|
||||
@Column({ type: 'jsonb', default: [] })
|
||||
histories: ArticleHistory[];
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
import { Column } from 'typeorm';
|
||||
import { ObjectType, Field } from '@nestjs/graphql';
|
||||
import { Article } from '../entities/article.entity';
|
||||
|
||||
@ObjectType()
|
||||
export class ArticleHistory {
|
||||
@Field(() => Object)
|
||||
payload: Partial<Article>;
|
||||
|
||||
updatedAt: Date;
|
||||
|
||||
automatic: boolean;
|
||||
|
||||
published: boolean;
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
import { ObjectScalar } from './scalars/object.scalar';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { PasswordConverter } from './services/password-converter';
|
||||
import { PubSubModule } from './pub-sub/pub-sub.module';
|
||||
import { AuthModule } from '@nestjs-lib/auth';
|
||||
|
||||
@Module({
|
||||
imports: [PubSubModule, AuthModule],
|
||||
providers: [PasswordConverter, ObjectScalar],
|
||||
exports: [PasswordConverter, AuthModule],
|
||||
providers: [PasswordConverter],
|
||||
exports: [PasswordConverter],
|
||||
imports: [PubSubModule],
|
||||
})
|
||||
export class CommonsModule {}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { readConfiguration } from '@fennec/configuration';
|
||||
import { readFileSync } from 'fs';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { join } from 'path';
|
||||
|
||||
export default () => {
|
||||
return readConfiguration({
|
||||
etcd: {
|
||||
hosts: '192.168.31.2:2379',
|
||||
},
|
||||
});
|
||||
return yaml.load(
|
||||
readFileSync(join(__dirname, '../../../config.yml'), 'utf8'),
|
||||
) as unknown;
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ export class AppBaseEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@CreateDateColumn({ select: false })
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ select: false })
|
||||
|
@ -4,7 +4,7 @@ import { PubSub } from './pub-sub';
|
||||
|
||||
debug.enable('app:pubsub:*');
|
||||
|
||||
describe.skip('PubSub', () => {
|
||||
describe('PubSub', () => {
|
||||
let instance: PubSub;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
@ -1,13 +0,0 @@
|
||||
import { Scalar, CustomScalar } from '@nestjs/graphql';
|
||||
import { GraphQLJSONObject } from 'graphql-type-json';
|
||||
|
||||
@Scalar('Object', (type) => Object)
|
||||
export class ObjectScalar implements CustomScalar<number, Object> {
|
||||
description = GraphQLJSONObject.description;
|
||||
|
||||
parseValue = GraphQLJSONObject.parseValue;
|
||||
|
||||
serialize = GraphQLJSONObject.serialize;
|
||||
|
||||
parseLiteral = GraphQLJSONObject.parseLiteral;
|
||||
}
|
@ -112,7 +112,7 @@ export class BaseDbService<Entity extends AppBaseEntity> extends TypeormHelper {
|
||||
}
|
||||
}
|
||||
|
||||
async canRemove(ids: string[]): Promise<void> {
|
||||
async canYouRemoveWithIds(ids: string[]): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { ServiceRegister } from '@fennec/configuration';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
@ -16,11 +15,6 @@ async function bootstrap() {
|
||||
}),
|
||||
);
|
||||
app.useGlobalFilters(new HttpExceptionFilter());
|
||||
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('blog/api', `http://localhost:${port}`);
|
||||
register.register('admin.blog/api', `http://localhost:${port}`);
|
||||
register.register('api.blog', `http://localhost:${port}`);
|
||||
await app.listen(configService.get<number>('http.port'));
|
||||
}
|
||||
bootstrap();
|
||||
|
@ -1,9 +0,0 @@
|
||||
import { InputType } from '@nestjs/graphql';
|
||||
import { IsString, Length } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class CreateTagInput {
|
||||
@IsString()
|
||||
@Length(1, 100)
|
||||
name: string;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { CreateTagInput } from './create-tag.input';
|
||||
import { InputType, PartialType } from '@nestjs/graphql';
|
||||
import { IsUUID } from 'class-validator';
|
||||
|
||||
@InputType()
|
||||
export class UpdateTagInput extends PartialType(CreateTagInput) {
|
||||
@IsUUID()
|
||||
id: string;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { AppBaseEntity } from './../../commons/entities/app-base-entity';
|
||||
import { ObjectType } from '@nestjs/graphql';
|
||||
import { Column, Index, Entity } from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
@ObjectType()
|
||||
export class Tag extends AppBaseEntity {
|
||||
@Index({ unique: true })
|
||||
@Column({ length: 100 })
|
||||
name: string;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { Tag } from './entities/tag.entity';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CommonsModule } from './../commons/commons.module';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TagsService } from './tags.service';
|
||||
import { TagsResolver } from './tags.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [CommonsModule, TypeOrmModule.forFeature([Tag])],
|
||||
providers: [TagsResolver, TagsService],
|
||||
})
|
||||
export class TagsModule {}
|
@ -1,25 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { TagsResolver } from './tags.resolver';
|
||||
import { TagsService } from './tags.service';
|
||||
|
||||
describe('TagsResolver', () => {
|
||||
let resolver: TagsResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
TagsResolver,
|
||||
{
|
||||
provide: TagsService,
|
||||
useValue: {},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<TagsResolver>(TagsResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(resolver).toBeDefined();
|
||||
});
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
|
||||
import { TagsService } from './tags.service';
|
||||
import { Tag } from './entities/tag.entity';
|
||||
import { CreateTagInput } from './dto/create-tag.input';
|
||||
import { UpdateTagInput } from './dto/update-tag.input';
|
||||
|
||||
@Resolver(() => Tag)
|
||||
export class TagsResolver {
|
||||
constructor(private readonly tagsService: TagsService) {}
|
||||
|
||||
@Mutation(() => Tag)
|
||||
createTag(@Args('createTagInput') createTagInput: CreateTagInput) {
|
||||
return this.tagsService.create(createTagInput);
|
||||
}
|
||||
|
||||
@Query(() => [Tag], { name: 'tags' })
|
||||
findAll() {
|
||||
return this.tagsService.findAll();
|
||||
}
|
||||
|
||||
@Query(() => Tag, { name: 'tag' })
|
||||
findOne(@Args('id', { type: () => String }) id: string) {
|
||||
return this.tagsService.findOne(id);
|
||||
}
|
||||
|
||||
@Mutation(() => Tag)
|
||||
async updateTag(@Args('updateTagInput') updateTagInput: UpdateTagInput) {
|
||||
const tag = await this.tagsService.findOne(updateTagInput.id);
|
||||
return this.tagsService.update(tag, updateTagInput);
|
||||
}
|
||||
|
||||
@Mutation(() => Tag)
|
||||
removeTag(@Args('id', { type: () => String }) id: string) {
|
||||
return this.tagsService.remove(id);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { Tag } from './entities/tag.entity';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { TagsService } from './tags.service';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
describe('TagsService', () => {
|
||||
let service: TagsService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
TagsService,
|
||||
{
|
||||
provide: getRepositoryToken(Tag),
|
||||
useValue: new Repository(),
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<TagsService>(TagsService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { BaseDbService } from './../commons/services/base-db.service';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateTagInput } from './dto/create-tag.input';
|
||||
import { UpdateTagInput } from './dto/update-tag.input';
|
||||
import { Tag } from './entities/tag.entity';
|
||||
|
||||
@Injectable()
|
||||
export class TagsService extends BaseDbService<Tag> {
|
||||
readonly uniqueFields: Array<keyof Tag> = ['name'];
|
||||
constructor(@InjectRepository(Tag) readonly repository: Repository<Tag>) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* create or recover a tag
|
||||
*/
|
||||
async create(createTagInput: CreateTagInput): Promise<Tag> {
|
||||
const old = await this.repository.findOne({ name: createTagInput.name });
|
||||
return this.repository.save(
|
||||
old
|
||||
? this.repository.merge(old, createTagInput)
|
||||
: this.repository.create(createTagInput),
|
||||
);
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
return await this.repository.find({
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
||||
async update(tag: Tag, updateTagInput: UpdateTagInput) {
|
||||
await this.isDuplicateEntityForUpdate(tag.id, updateTagInput);
|
||||
return await this.repository.save(
|
||||
this.repository.merge(tag, updateTagInput),
|
||||
);
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
await this.canRemove([id]);
|
||||
return await this.repository.softDelete({ id }).then((d) => d.affected);
|
||||
}
|
||||
}
|
@ -1,49 +1,24 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import {
|
||||
ApolloServerTestClient,
|
||||
createTestClient,
|
||||
} from 'apollo-server-testing';
|
||||
import { gql } from 'apollo-server-express';
|
||||
|
||||
describe('ArticleResolver (e2e)', () => {
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
let apolloClient: ApolloServerTestClient;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
const module: GraphQLModule = moduleFixture.get<GraphQLModule>(
|
||||
GraphQLModule,
|
||||
);
|
||||
// apolloServer is protected, we need to cast module to any to get it
|
||||
apolloClient = createTestClient((module as any).apolloServer);
|
||||
});
|
||||
it('QUERY hello', async () => {
|
||||
const res = await apolloClient.query({
|
||||
query: gql`
|
||||
query {
|
||||
hello {
|
||||
message
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {},
|
||||
});
|
||||
expect(res.data).toEqual({
|
||||
hello: {
|
||||
message: 'Hello, World!',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app?.close();
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
|
@ -1,33 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import {
|
||||
ApolloServerTestClient,
|
||||
createTestClient,
|
||||
} from 'apollo-server-testing';
|
||||
import { gql } from 'apollo-server-express';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
let apolloClient: ApolloServerTestClient;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
const module: GraphQLModule = moduleFixture.get<GraphQLModule>(
|
||||
GraphQLModule,
|
||||
);
|
||||
// apolloServer is protected, we need to cast module to any to get it
|
||||
apolloClient = createTestClient((module as any).apolloServer);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app?.close();
|
||||
});
|
||||
});
|
8
test/data/bad-work.js
Normal file
8
test/data/bad-work.js
Normal file
@ -0,0 +1,8 @@
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
console.log(i * 10);
|
||||
}
|
||||
console.error('Error Message');
|
||||
console.error('Error Message 2');
|
||||
console.log('Bye-bye');
|
||||
|
||||
process.exit(1);
|
115
test/data/gitea-hook-payload.json.bin
Normal file
115
test/data/gitea-hook-payload.json.bin
Normal file
@ -0,0 +1,115 @@
|
||||
{
|
||||
"secret": "boardcat",
|
||||
"ref": "refs/heads/master",
|
||||
"before": "429de1eaedf1da83f1e0e3ac3d8b20e771b7051c",
|
||||
"after": "429de1eaedf1da83f1e0e3ac3d8b20e771b7051c",
|
||||
"compare_url": "",
|
||||
"commits": [
|
||||
{
|
||||
"id": "429de1eaedf1da83f1e0e3ac3d8b20e771b7051c",
|
||||
"message": "test(pipeline-tasks): pass test cases.\n",
|
||||
"url": "https://git.ivanli.cc/Fennec/fennec-be/commit/429de1eaedf1da83f1e0e3ac3d8b20e771b7051c",
|
||||
"author": {
|
||||
"name": "Ivan",
|
||||
"email": "ivanli@live.cn",
|
||||
"username": ""
|
||||
},
|
||||
"committer": {
|
||||
"name": "Ivan",
|
||||
"email": "ivanli@live.cn",
|
||||
"username": ""
|
||||
},
|
||||
"verification": null,
|
||||
"timestamp": "0001-01-01T00:00:00Z",
|
||||
"added": null,
|
||||
"removed": null,
|
||||
"modified": null
|
||||
}
|
||||
],
|
||||
"head_commit": null,
|
||||
"repository": {
|
||||
"id": 3,
|
||||
"owner": {
|
||||
"id": 3,
|
||||
"login": "Fennec",
|
||||
"full_name": "",
|
||||
"email": "",
|
||||
"avatar_url": "https://git.ivanli.cc/user/avatar/Fennec/-1",
|
||||
"language": "",
|
||||
"is_admin": false,
|
||||
"last_login": "1970-01-01T08:00:00+08:00",
|
||||
"created": "2021-01-30T16:46:11+08:00",
|
||||
"username": "Fennec"
|
||||
},
|
||||
"name": "fennec-be",
|
||||
"full_name": "Fennec/fennec-be",
|
||||
"description": "Fennec CI/CD Back-End",
|
||||
"empty": false,
|
||||
"private": false,
|
||||
"fork": false,
|
||||
"template": false,
|
||||
"parent": null,
|
||||
"mirror": false,
|
||||
"size": 1897,
|
||||
"html_url": "https://git.ivanli.cc/Fennec/fennec-be",
|
||||
"ssh_url": "ssh://gitea@git.ivanli.cc:7018/Fennec/fennec-be.git",
|
||||
"clone_url": "https://git.ivanli.cc/Fennec/fennec-be.git",
|
||||
"original_url": "",
|
||||
"website": "",
|
||||
"stars_count": 1,
|
||||
"forks_count": 0,
|
||||
"watchers_count": 1,
|
||||
"open_issues_count": 0,
|
||||
"open_pr_counter": 0,
|
||||
"release_counter": 0,
|
||||
"default_branch": "master",
|
||||
"archived": false,
|
||||
"created_at": "2021-01-31T09:58:38+08:00",
|
||||
"updated_at": "2021-03-27T15:57:00+08:00",
|
||||
"permissions": {
|
||||
"admin": false,
|
||||
"push": false,
|
||||
"pull": false
|
||||
},
|
||||
"has_issues": true,
|
||||
"internal_tracker": {
|
||||
"enable_time_tracker": true,
|
||||
"allow_only_contributors_to_track_time": true,
|
||||
"enable_issue_dependencies": true
|
||||
},
|
||||
"has_wiki": true,
|
||||
"has_pull_requests": true,
|
||||
"has_projects": true,
|
||||
"ignore_whitespace_conflicts": false,
|
||||
"allow_merge_commits": true,
|
||||
"allow_rebase": true,
|
||||
"allow_rebase_explicit": true,
|
||||
"allow_squash_merge": true,
|
||||
"avatar_url": "",
|
||||
"internal": false
|
||||
},
|
||||
"pusher": {
|
||||
"id": 1,
|
||||
"login": "Ivan",
|
||||
"full_name": "Ivan Li",
|
||||
"email": "ivan@noreply.%(DOMAIN)s",
|
||||
"avatar_url": "https://git.ivanli.cc/user/avatar/Ivan/-1",
|
||||
"language": "zh-CN",
|
||||
"is_admin": true,
|
||||
"last_login": "2021-03-26T22:28:05+08:00",
|
||||
"created": "2021-01-23T18:15:30+08:00",
|
||||
"username": "Ivan"
|
||||
},
|
||||
"sender": {
|
||||
"id": 1,
|
||||
"login": "Ivan",
|
||||
"full_name": "Ivan Li",
|
||||
"email": "ivan@noreply.%(DOMAIN)s",
|
||||
"avatar_url": "https://git.ivanli.cc/user/avatar/Ivan/-1",
|
||||
"language": "zh-CN",
|
||||
"is_admin": true,
|
||||
"last_login": "2021-03-26T22:28:05+08:00",
|
||||
"created": "2021-01-23T18:15:30+08:00",
|
||||
"username": "Ivan"
|
||||
}
|
||||
}
|
7
test/data/one-second-work.js
Normal file
7
test/data/one-second-work.js
Normal file
@ -0,0 +1,7 @@
|
||||
let timer;
|
||||
let count = 0;
|
||||
setTimeout(() => clearInterval(timer), 1_000);
|
||||
|
||||
timer = setInterval(() => {
|
||||
console.log(++count * 10);
|
||||
}, 95);
|
@ -1,15 +0,0 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const transformer = require('@nestjs/graphql/plugin');
|
||||
|
||||
module.exports.name = 'nestjs-graphql-transformer';
|
||||
// you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
|
||||
module.exports.version = 1;
|
||||
|
||||
module.exports.factory = (cs) => {
|
||||
return transformer.before(
|
||||
{
|
||||
// @nestjs/graphql/plugin options (can be empty)
|
||||
},
|
||||
cs.tsCompiler.program,
|
||||
);
|
||||
};
|
@ -5,12 +5,5 @@
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"astTransformers": {
|
||||
"before": ["<rootDir>/graphql-e2e.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,10 @@
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["es2020"],
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true
|
||||
"incremental": true
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user