-
Notifications
You must be signed in to change notification settings - Fork 57
feat(rest): Created rest package #200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
dbfca70
49cd3a4
488a39c
24a6196
8178f02
4529d18
f16f6c6
612a43a
0d0b9f5
221ca7b
f8daf99
064a447
49b16fd
5bd8ccb
9636397
dc97f26
887910b
3eb2454
45932c9
85393ad
d4d4553
7245c19
ad5c987
f1ef57f
67a9196
b70eacd
697e90f
87b7c05
4111575
7d46be2
ee816fb
465589a
84a6b2e
d3cfb9f
1a33964
e2353ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"presets": [ | ||
[ | ||
"@nrwl/web/babel", | ||
{ | ||
"useBuiltIns": "usage" | ||
} | ||
] | ||
] | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,36 @@ | ||||||||||||||||||||
{ | ||||||||||||||||||||
"extends": [ | ||||||||||||||||||||
"../../.eslintrc.json" | ||||||||||||||||||||
], | ||||||||||||||||||||
"ignorePatterns": [ | ||||||||||||||||||||
"!**/*" | ||||||||||||||||||||
], | ||||||||||||||||||||
Comment on lines
+5
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dangerous ignore pattern: this disables all ignores (may lint node_modules/dist).
Apply: - "ignorePatterns": [
- "!**/*"
- ],
+ "ignorePatterns": [
+ "dist/",
+ "node_modules/",
+ "coverage/",
+ "**/*.d.ts"
+ ], 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||
"parserOptions": { | ||||||||||||||||||||
"project": "./tsconfig.json" | ||||||||||||||||||||
}, | ||||||||||||||||||||
"overrides": [ | ||||||||||||||||||||
{ | ||||||||||||||||||||
"files": [ | ||||||||||||||||||||
"*.ts", | ||||||||||||||||||||
"*.tsx", | ||||||||||||||||||||
"*.js", | ||||||||||||||||||||
"*.jsx" | ||||||||||||||||||||
], | ||||||||||||||||||||
"rules": {} | ||||||||||||||||||||
}, | ||||||||||||||||||||
{ | ||||||||||||||||||||
"files": [ | ||||||||||||||||||||
"*.ts", | ||||||||||||||||||||
"*.tsx" | ||||||||||||||||||||
], | ||||||||||||||||||||
"rules": {} | ||||||||||||||||||||
}, | ||||||||||||||||||||
{ | ||||||||||||||||||||
"files": [ | ||||||||||||||||||||
"*.js", | ||||||||||||||||||||
"*.jsx" | ||||||||||||||||||||
], | ||||||||||||||||||||
"rules": {} | ||||||||||||||||||||
} | ||||||||||||||||||||
] | ||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<p align="center"> | ||
<a href="https://tripss.github.io/nestjs-query" target="blank"><img src="https://tripss.github.io/nestjs-query/img/logo.svg" width="120" alt="Nestjs-query Logo" /></a> | ||
</p> | ||
|
||
[](https://www.npmjs.org/package/@ptc-org/nestjs-query-rest) | ||
[](https://github.com/tripss/nestjs-query/actions?query=workflow%3ATest+and+branch%3Amaster+) | ||
[](https://codecov.io/gh/TriPSs/nestjs-query) | ||
[](https://snyk.io/test/github/tripss/nestjs-query?targetFile=packages/query-rest/package.json) | ||
|
||
# `@ptc-org/nestjs-query-rest` | ||
|
||
This package provides a code first implementation of rest CRUD endpoints. It is built on top of | ||
of [nestjs](https://nestjs.com/). | ||
|
||
## Installation | ||
|
||
[Install Guide](https://tripss.github.io/nestjs-query/docs/introduction/install) | ||
|
||
## Getting Started | ||
|
||
The get started with the `@ptc-org/nestjs-query-rest` package checkout | ||
the [Getting Started](https://tripss.github.io/nestjs-query/docs/rest/getting-started) docs. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* eslint-disable */ | ||
// eslint-disable-next-line import/no-default-export | ||
export default { | ||
displayName: 'query-rest', | ||
preset: '../../jest.preset.js', | ||
globals: {}, | ||
testEnvironment: 'node', | ||
transform: { | ||
'^.+\\.[tj]sx?$': [ | ||
'ts-jest', | ||
{ | ||
tsconfig: '<rootDir>/tsconfig.spec.json' | ||
} | ||
] | ||
}, | ||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], | ||
coverageDirectory: '../../coverage/packages/query-rest' | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"name": "@ptc-org/nestjs-query-rest", | ||
"version": "4.3.0", | ||
"description": "Nestjs rest query adapter", | ||
"author": "doug-martin <[email protected]>", | ||
"homepage": "https://github.com/tripss/nestjs-query#readme", | ||
"keywords": [ | ||
"rest", | ||
"crud", | ||
"nestjs" | ||
], | ||
"license": "MIT", | ||
"main": "src/index.js", | ||
"types": "src/index.d.ts", | ||
"directories": { | ||
"lib": "src", | ||
"test": "__tests__" | ||
}, | ||
"files": [ | ||
"src/**" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/tripss/nestjs-query.git", | ||
"directory": "packages/query-rest" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/tripss/nestjs-query/issues" | ||
}, | ||
"dependencies": { | ||
"lodash.omit": "^4.5.0", | ||
"lower-case-first": "^2.0.2", | ||
"pluralize": "^8.0.0", | ||
"tslib": "^2.6.2", | ||
"upper-case-first": "^2.0.2" | ||
}, | ||
"peerDependencies": { | ||
"@nestjs/common": "^9.0.0 || ^10.0.0", | ||
"@nestjs/core": "^9.0.0 || ^10.0.0", | ||
"@nestjs/graphql": "^11.0.0 || ^12.0.0", | ||
"@nestjs/swagger": "^7.0.0", | ||
"class-transformer": "^0.5", | ||
"class-validator": "^0.14.0", | ||
"ts-morph": "^19.0.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"name": "query-rest", | ||
"$schema": "../../node_modules/nx/schemas/project-schema.json", | ||
"sourceRoot": "packages/query-rest/src", | ||
"projectType": "library", | ||
"targets": { | ||
"lint": { | ||
"executor": "@nx/eslint:lint", | ||
"outputs": ["{options.outputFile}"], | ||
"options": { | ||
"lintFilePatterns": ["packages/query-rest/**/*.ts"] | ||
} | ||
}, | ||
"test": { | ||
"executor": "@nx/jest:jest", | ||
"outputs": ["{workspaceRoot}/coverage/packages/query-rest"], | ||
"options": { | ||
"jestConfig": "packages/query-rest/jest.config.ts", | ||
"passWithNoTests": true | ||
} | ||
}, | ||
"build": { | ||
"executor": "@nx/js:tsc", | ||
"outputs": ["{options.outputPath}"], | ||
"options": { | ||
"outputPath": "dist/packages/query-rest", | ||
"tsConfig": "packages/query-rest/tsconfig.lib.json", | ||
"packageJson": "packages/query-rest/package.json", | ||
"main": "packages/query-rest/src/index.ts", | ||
"assets": ["packages/query-rest/*.md"], | ||
"updateBuildableProjectDepsInPackageJson": true, | ||
"buildableProjectDepsInPackageJsonType": "dependencies" | ||
} | ||
}, | ||
"version": { | ||
"executor": "@jscutlery/semver:version", | ||
"options": {} | ||
}, | ||
"publish": { | ||
"executor": "nx:run-commands", | ||
"options": { | ||
"command": "npm publish ./dist/packages/query-rest --access public" | ||
} | ||
} | ||
}, | ||
"tags": [], | ||
"implicitDependencies": ["core"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { Filter } from '@ptc-org/nestjs-query-core' | ||
|
||
export enum OperationGroup { | ||
READ = 'read', | ||
AGGREGATE = 'aggregate', | ||
CREATE = 'create', | ||
UPDATE = 'update', | ||
DELETE = 'delete' | ||
} | ||
|
||
export interface AuthorizationContext { | ||
/** The name of the method that uses the @AuthorizeFilter decorator */ | ||
readonly operationName: string | ||
|
||
/** The group this operation belongs to */ | ||
readonly operationGroup: OperationGroup | ||
|
||
/** If the operation does not modify any entities */ | ||
readonly readonly: boolean | ||
|
||
/** If the operation can affect multiple entities */ | ||
readonly many: boolean | ||
} | ||
|
||
export interface CustomAuthorizer<DTO> { | ||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any | ||
authorize(context: any, authorizerContext: AuthorizationContext): Promise<Filter<DTO>> | ||
|
||
authorizeRelation?( | ||
relationName: string, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
context: any, | ||
authorizerContext: AuthorizationContext | ||
): Promise<Filter<unknown> | undefined> | ||
} | ||
|
||
export interface Authorizer<DTO> extends CustomAuthorizer<DTO> { | ||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any | ||
authorize(context: any, authorizerContext: AuthorizationContext): Promise<Filter<DTO>> | ||
|
||
authorizeRelation( | ||
relationName: string, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
context: any, | ||
authorizerContext: AuthorizationContext | ||
): Promise<Filter<unknown> | undefined> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { Inject, Injectable, Optional } from '@nestjs/common' | ||
import { ModuleRef } from '@nestjs/core' | ||
import { Class, Filter } from '@ptc-org/nestjs-query-core' | ||
|
||
// import { getAuthorizer } from '../decorators' | ||
// import { ResolverRelation } from '../resolvers/relations' | ||
import { AuthorizationContext, Authorizer, CustomAuthorizer } from './authorizer' | ||
import { getCustomAuthorizerToken } from './tokens' | ||
|
||
export interface AuthorizerOptions<DTO> { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
authorize: (context: any, authorizationContext: AuthorizationContext) => Filter<DTO> | Promise<Filter<DTO>> | ||
} | ||
|
||
// const createRelationAuthorizer = (opts: AuthorizerOptions<unknown>): Authorizer<unknown> => ({ | ||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
// async authorize(context: any, authorizationContext: AuthorizationContext): Promise<Filter<unknown>> { | ||
// return opts.authorize(context, authorizationContext) ?? {} | ||
// }, | ||
// authorizeRelation(): Promise<Filter<unknown>> { | ||
// return Promise.reject(new Error('Not implemented')) | ||
// } | ||
// }) | ||
|
||
export function createDefaultAuthorizer<DTO>( | ||
DTOClass: Class<DTO>, | ||
opts?: CustomAuthorizer<DTO> | AuthorizerOptions<DTO> // instance of class or authorizer options | ||
): Class<Authorizer<DTO>> { | ||
@Injectable() | ||
class DefaultAuthorizer implements Authorizer<DTO> { | ||
readonly authOptions?: AuthorizerOptions<DTO> | CustomAuthorizer<DTO> = opts | ||
|
||
readonly relationsAuthorizers: Map<string, Authorizer<unknown> | undefined> | ||
|
||
// private readonly relations: Map<string, ResolverRelation<unknown>> | ||
|
||
constructor( | ||
private readonly moduleRef: ModuleRef, | ||
@Optional() @Inject(getCustomAuthorizerToken(DTOClass)) private readonly customAuthorizer?: CustomAuthorizer<DTO> | ||
) { | ||
this.relationsAuthorizers = new Map<string, Authorizer<unknown> | undefined>() | ||
// this.relations = this.getRelations() | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
public async authorize(context: any, authorizationContext: AuthorizationContext): Promise<Filter<DTO>> { | ||
return ( | ||
this.customAuthorizer?.authorize(context, authorizationContext) ?? | ||
this.authOptions?.authorize(context, authorizationContext) ?? | ||
{} | ||
) | ||
} | ||
|
||
public async authorizeRelation( | ||
relationName: string, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
context: any, | ||
authorizationContext: AuthorizationContext | ||
): Promise<Filter<unknown>> { | ||
if (this.customAuthorizer && typeof this.customAuthorizer.authorizeRelation === 'function') { | ||
const filterFromCustomAuthorizer = await this.customAuthorizer.authorizeRelation( | ||
relationName, | ||
context, | ||
authorizationContext | ||
) | ||
|
||
if (filterFromCustomAuthorizer) { | ||
return filterFromCustomAuthorizer | ||
} | ||
} | ||
return {} | ||
// this.addRelationAuthorizerIfNotExist(relationName) | ||
// return this.relationsAuthorizers.get(relationName)?.authorize(context, authorizationContext) ?? {} | ||
} | ||
|
||
private addRelationAuthorizerIfNotExist(relationName: string) { | ||
if (!this.relationsAuthorizers.has(relationName)) { | ||
return | ||
// const relation = this.relations.get(relationName) | ||
// if (!relation) return | ||
// if (relation.auth) { | ||
// this.relationsAuthorizers.set(relationName, createRelationAuthorizer(relation.auth)) | ||
// } else if (getAuthorizer(relation.DTO)) { | ||
// this.relationsAuthorizers.set(relationName, this.moduleRef.get(getAuthorizerToken(relation.DTO), { strict: false })) | ||
// } | ||
} | ||
} | ||
|
||
// private getRelations(): Map<string, ResolverRelation<unknown>> { | ||
// const { many = {}, one = {} } = {}// getRelations(DTOClass) | ||
// const relationsMap = new Map<string, ResolverRelation<unknown>>() | ||
// Object.keys(many).forEach((relation) => relationsMap.set(relation, many[relation])) | ||
// Object.keys(one).forEach((relation) => relationsMap.set(relation, one[relation])) | ||
// return relationsMap | ||
// } | ||
} | ||
|
||
return DefaultAuthorizer | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './authorizer' | ||
export * from './default-crud.authorizer' | ||
export * from './tokens' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Switch to Node preset and avoid injecting polyfills in a library.
This package is server‑side NestJS;
@nrwl/web/babel
plususeBuiltIns: "usage"
will pull browser polyfills and mutate globals, which is undesirable for a published library. Prefer the Node preset and disable polyfill injection; let consuming apps polyfill.Apply:
If you intentionally target browsers from this package, keep the web preset but add an explicit core‑js version:
Run to verify engines and dependencies:
🏁 Script executed:
Length of output: 1257
🏁 Script executed:
Length of output: 3046
Switch to the Node Babel preset and stop injecting browser polyfills in library packages.
This repo's packages are server-side NestJS libraries; using @nrwl/web/babel with "useBuiltIns":"usage" pulls browser polyfills and requires core-js (core-js is not declared in any package.json). Replace the web preset with the node preset and disable polyfill injection — or, if you truly target browsers from this package, keep the web preset but add corejs: 3.
Apply:
If this package intentionally targets browsers, instead add core-js:
Files to update: packages/query-rest/.babelrc (also update the same pattern in packages/core/.babelrc, packages/query-graphql/.babelrc, packages/query-mongoose/.babelrc, packages/query-sequelize/.babelrc, packages/query-typegoose/.babelrc, packages/query-typeorm/.babelrc).
📝 Committable suggestion