Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/Pre-Release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Pre-Release
on:
push:
branches:
- next
- beta
- "*.x" # maintenance releases branches
env:
BEFORE_SHA: ${{ github.event.before }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 14
- uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('./package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
- name: Test
run: npx nx affected:test --base=remotes/origin/master
- name: Deploy
run: npx nx affected --target=deploy --base=remotes/origin/master

83 changes: 69 additions & 14 deletions libs/json-api-nestjs/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

****strong text****
# json-api-nestjs

## Installation
Expand All @@ -25,7 +25,7 @@ import { Users } from 'database';
})
export class AppModule {}
```
After this, you have prepare crude with ready to use end point:
After this, you have to prepare CRUDs with ready-to-use endpoints:


- GET /users
Expand Down Expand Up @@ -53,7 +53,7 @@ export interface ModuleOptions {
};
}
```
You can extend default controller:
You can extend the default controller:
```typescript
import { Get, Param, Inject, BadRequestException } from '@nestjs/common';

Expand Down Expand Up @@ -90,24 +90,79 @@ export class ExtendUserController extends JsonBaseController<Users> {
}
```

You can overwrite the default config for the current controller using options in the decorator **JsonAPi**.
Also you can specify an API method necessary for you, using **allowMethod**

## Swagger UI

For using swagger, you should only add [@nestjs/swagger](https://docs.nestjs.com/openapi/introduction)

## Available endpoint method
Using **Users** entity and relation **Roles** entity as example

### List item of Users

```
GET /users
```
Available query params:

- **include** - you can extend result with relations (aka join)
```
GET /users?include=roles
```
result of request will have role relation for each **Users** item

- **fields** - you can specify required fields of result query

```
GET /users?fields[target]=login,lastName&fileds[roles]=name,key
```
The "target" is **Users** entity
The "roles" is **Roles** entity
So, result of request will be have only fields *login* and *lastName* for **Users** entity and fields *name* and *key* for **Roles** entity
- **sort** - you can sort result of the request

```
GET /users?sort=target.name,-roles.key
```
The "target" is **Users** entity
The "roles" is **Roles** entity
So, result of the request will be sorted by field *name* of **Users** by *ASC* and field *key* of **Roles** entity by **DESC**.
- **page** - pagination for you request

```
GET /users?page[number]=1page[size]=20
```
- **filter** - filter for query

```
GET /users?filter[name][eq]=1&filter[roles.name][ne]=test&filter[roles.status][eq]=true
```
The "name" is a field of **Users** entity
The "roles.name" is *name* field of **Roles** entity
The "eq", "ne" is *[Filter operand](#filter-operand)*

So, this query will be transformed like sql:
```sql
WHERE users.name = 1 AND roles.name <> 'test' AND roles.status = true
```

## Filter operand

```typescript
type FilterOperand = {
in: string[], // is equal to the conditional of query "WHERE 'attribute_name' IN ('value1', 'value2')"
nin: string[], // is equal to the conditional of query "WHERE 'attribute_name' NOT IN ('value1', 'value1')"
eq: string, // is equal to the conditional of query "WHERE 'attribute_name' = 'value1'
ne: string, // is equal to the conditional of query "WHERE 'attribute_name' <> 'value1'
gte: string, // is equal to the conditional of query "WHERE 'attribute_name' >= 'value1'
gt: string, // is equal to the conditional of query "WHERE 'attribute_name' > 'value1'
lt: string, // is equal to the conditional of query "WHERE 'attribute_name' < 'value1'
lte:string, // is equal to the conditional of query "WHERE 'attribute_name' <= 'value1'
regexp: string, // is equal to the conditional of query "WHERE 'attribute_name' ~* value1
some: string, // is equal to the conditional of query "WHERE 'attribute_name' && [value1]
type FilterOperand {
in: string[] // is equal to the conditional of query "WHERE 'attribute_name' IN ('value1', 'value2')"
nin: string[] // is equal to the conditional of query "WHERE 'attribute_name' NOT IN ('value1', 'value1')"
eq: string // is equal to the conditional of query "WHERE 'attribute_name' = 'value1'
ne: string // is equal to the conditional of query "WHERE 'attribute_name' <> 'value1'
gte: string // is equal to the conditional of query "WHERE 'attribute_name' >= 'value1'
gt: string // is equal to the conditional of query "WHERE 'attribute_name' > 'value1'
lt: string // is equal to the conditional of query "WHERE 'attribute_name' < 'value1'
lte:string // is equal to the conditional of query "WHERE 'attribute_name' <= 'value1'
regexp: string // is equal to the conditional of query "WHERE 'attribute_name' ~* value1
some: string // is equal to the conditional of query "WHERE 'attribute_name' && [value1]
}
```


20 changes: 16 additions & 4 deletions libs/json-api-nestjs/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
"projectType": "library",
"targets": {
"deploy": {
"executor": "@ng-builders/semrel:release",
"dependsOn": [{ "projects": "self", "target": "release" }],
"executor": "./dist/libs/semrel:release",
"dependsOn": [
{ "projects": "self", "target": "buildSemRel"},
{ "projects": "self", "target": "release" }
],
"options": {
"npm": {
"pkgRoot": "./dist/libs/json-api-nestjs"
Expand All @@ -24,6 +27,16 @@
"parallel": false
}
},
"buildSemRel": {
"executor": "nx:run-commands",
"options": {
"commands": [
"npx nx build semrel"
],
"cwd": "./",
"parallel": false
}
},
"build": {
"executor": "@nrwl/js:tsc",
"outputs": [
Expand Down Expand Up @@ -73,6 +86,5 @@
}
}
},
"tags": [],
"implicitDependencies": ["sevrel"]
"tags": []
}
1 change: 1 addition & 0 deletions libs/json-api-nestjs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './lib/json-api.module';
export * from './lib/decorators';
export { excludeMethod } from './lib/config/bindings';
export * from './lib/types';
export * from './lib/types-common';
export * from './lib/mixin/controller';
4 changes: 2 additions & 2 deletions libs/json-api-nestjs/src/lib/constants/reflection.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export const JSON_API_DECORATOR_ENTITY = Symbol('JSON_API_ENTITY');
export const JSON_API_DECORATOR_OPTIONS = Symbol('JSON_API_OPTIONS');
export const GLOBAL_MODULE_OPTIONS_TOKEN = Symbol('GLOBAL_MODULE_OPTIONS');
export const CURRENT_DATA_SOURCE_TOKEN = Symbol('CURRENT_DATA_SOURCE_TOKEN');

export const PARAMS_RESOURCE_ID = 'id';
export const PARAMS_RELATION_ID = 'relId';
export const PARAMS_RELATION_NAME = 'relName';

export const JSON_API_CONFIG = 'JSON_API_CONFIG';

export const GLOBAL_MODULE_OPTIONS_TOKEN = Symbol('GLOBAL_MODULE_OPTIONS');

export const JSON_API_SERVICE_POSTFIX = 'JsonApiService';
export const CONFIG_PARAM_POSTFIX = 'JsonApiConfigParam';
export const JSON_API_CONTROLLER_POSTFIX = 'JsonApiController';
Expand Down
12 changes: 9 additions & 3 deletions libs/json-api-nestjs/src/lib/factory/ajv/ajv.factory.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import AjvCall from 'ajv';
import { FactoryProvider } from '@nestjs/common/interfaces/modules/provider.interface';
import { getDataSourceToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { plainToClass } from 'class-transformer';

import { ModuleOptions } from '../../types';
import { GLOBAL_MODULE_OPTIONS_TOKEN } from '../../constants';
import {
CURRENT_DATA_SOURCE_TOKEN,
GLOBAL_MODULE_OPTIONS_TOKEN,
} from '../../constants';

import {
inputQuerySchema,
inputBodyPostSchema,
Expand Down Expand Up @@ -141,7 +144,10 @@ export const ajvFactory: FactoryProvider<AjvCall> = {
provide: AjvCall,
useFactory: AjvCallFactory,
inject: [
getDataSourceToken(),
{
token: CURRENT_DATA_SOURCE_TOKEN,
optional: false,
},
{
token: GLOBAL_MODULE_OPTIONS_TOKEN,
optional: false,
Expand Down
15 changes: 13 additions & 2 deletions libs/json-api-nestjs/src/lib/json-api-nestjs-common.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { DynamicModule, Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { getDataSourceToken, TypeOrmModule } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';

import { ModuleOptions } from '../lib/types';
import { GLOBAL_MODULE_OPTIONS_TOKEN } from './constants';
import {
CURRENT_DATA_SOURCE_TOKEN,
GLOBAL_MODULE_OPTIONS_TOKEN,
} from './constants';
import { ajvFactory } from './factory';
import { ErrorInterceptors } from './mixin/interceptors';

Expand All @@ -14,6 +18,12 @@ export class JsonApiNestJsCommonModule {
useValue: options,
};

const currentDataSourceProvider = {
provide: CURRENT_DATA_SOURCE_TOKEN,
useFactory: (dataSource: DataSource) => dataSource,
inject: [getDataSourceToken(options.connectionName)],
};

const typeOrmModule = TypeOrmModule.forFeature(
options.entities,
options.connectionName
Expand All @@ -25,6 +35,7 @@ export class JsonApiNestJsCommonModule {
...(options.providers || []),
ajvFactory,
optionProvider,
currentDataSourceProvider,
ErrorInterceptors,
],
exports: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
JsonApiServiceMethode,
QueryParams,
} from '../../../types';
import { ResourceRequestObject } from '../../../types-common/request';
import { ResourceRequestObject } from '../../../types-common';
import { Relationship } from '../../../types-common';

export class JsonBaseController<Entity> implements ControllerTypes<Entity> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';

import {
CURRENT_DATA_SOURCE_TOKEN,
DEFAULT_CONNECTION_NAME,
GLOBAL_MODULE_OPTIONS_TOKEN,
} from '../../../constants';
Expand Down Expand Up @@ -35,12 +36,22 @@ describe('BodyInputPatchPipe', () => {
connectionName: DEFAULT_CONNECTION_NAME,
},
},
{
provide: CURRENT_DATA_SOURCE_TOKEN,
useFactory: (dataSource: DataSource) => dataSource,
inject: [getDataSourceToken(mockConnectionName)],
},
{
provide: getRepositoryToken(Users, mockConnectionName),
useFactory(dataSource: DataSource) {
return dataSource.getRepository<Users>(Users);
},
inject: [getDataSourceToken()],
inject: [
{
token: CURRENT_DATA_SOURCE_TOKEN,
optional: false,
},
],
},
],
}).compile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';

import {
CURRENT_DATA_SOURCE_TOKEN,
DEFAULT_CONNECTION_NAME,
GLOBAL_MODULE_OPTIONS_TOKEN,
} from '../../../constants';
Expand All @@ -29,15 +30,25 @@ describe('BodyInputPostPipe', () => {
provide: GLOBAL_MODULE_OPTIONS_TOKEN,
useValue: {
entities: entities,
connectionName: DEFAULT_CONNECTION_NAME,
connectionName: mockConnectionName,
},
},
{
provide: CURRENT_DATA_SOURCE_TOKEN,
useFactory: (dataSource: DataSource) => dataSource,
inject: [getDataSourceToken(mockConnectionName)],
},
{
provide: getRepositoryToken(Users, mockConnectionName),
useFactory(dataSource: DataSource) {
return dataSource.getRepository<Users>(Users);
},
inject: [getDataSourceToken()],
inject: [
{
token: CURRENT_DATA_SOURCE_TOKEN,
optional: false,
},
],
},
],
}).compile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import { ajvFactory } from '../../../factory';
import { entities, mockDBTestModule, Users } from '../../../mock-utils';
import { BodyRelationshipPatchPipe } from './body-relationship-patch.pipe';
import {
CURRENT_DATA_SOURCE_TOKEN,
DEFAULT_CONNECTION_NAME,
GLOBAL_MODULE_OPTIONS_TOKEN,
} from '../../../constants';
import { BadRequestException } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { getDataSourceToken, getRepositoryToken } from '@nestjs/typeorm';

describe('BodyRelationshipPatchPipe', () => {
let pipe: BodyRelationshipPatchPipe<Users>;
Expand All @@ -25,6 +28,23 @@ describe('BodyRelationshipPatchPipe', () => {
connectionName: DEFAULT_CONNECTION_NAME,
},
},
{
provide: CURRENT_DATA_SOURCE_TOKEN,
useFactory: (dataSource: DataSource) => dataSource,
inject: [getDataSourceToken(DEFAULT_CONNECTION_NAME)],
},
{
provide: getRepositoryToken(Users, DEFAULT_CONNECTION_NAME),
useFactory(dataSource: DataSource) {
return dataSource.getRepository<Users>(Users);
},
inject: [
{
token: CURRENT_DATA_SOURCE_TOKEN,
optional: false,
},
],
},
],
}).compile();

Expand Down
Loading