Skip to content

Several features for the node-boot persistence layer #19

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

Merged
merged 17 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .prettierrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ singleQuote: false
trailingComma: "all"
arrowParens: "avoid"
bracketSpacing: false
printWidth: 120
55 changes: 55 additions & 0 deletions app-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
node-boot:
# App configurations
application:
name: ""
platform: ""
environment: ""
defaultErrorHandler: false
port: 3000
api:
routePrefix: ""
nullResultCode: 200
undefinedResultCode: 200
paramOptions:
required: false
validations:
enable: true
enableDebugMessages: false
skipUndefinedProperties: false
skipNullProperties: false
skipMissingProperties: false
whitelist: false
forbidNonWhitelisted: false
forbidUnknownValues: false
stopAtFirstError: false

# Server configurations
server:
cors:
origin: "*"
methods:
- GET
- POST
- DELETE
- PUT
credentials: true
maxAge: 55000
cacheControl: 4096
preflightContinue: true
optionsSuccessStatus: 204
preflight: true
strictPreflight: false
exposedHeaders: []
allowedHeaders: []
multipart:
throwFileSizeLimit: true
limits:
fieldNameSize: 128
fieldSize: 128
fields: 10
fileSize: 4096
files: 5
headerPairs: 10

# Storage configurations
persistence:
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"pnpm": ">=7.5.1"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"types": "src/index.ts",
"scripts": {
"build": "turbo run build",
"build:clean": "turbo run clean:build",
Expand Down Expand Up @@ -57,7 +57,12 @@
"prettier": "^2.8.3",
"prettier-plugin-organize-imports": "^3.2.3",
"rimraf": "^4.3.1",
"turbo": "^1.10.12",
"turbo": "^1.11.2",
"typescript": "^5.1.6"
}
},
"files": [
"dist",
"config.d.ts"
],
"configSchema": "config.d.ts"
}
1 change: 0 additions & 1 deletion packages/authorization/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/** @type {import('jest').Config} */
module.exports = {
transform: {
"^.+\\.(t|j)sx?$": "@swc/jest",
Expand Down
10 changes: 4 additions & 6 deletions packages/authorization/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"main": "src/index.js",
"types": "src/index.ts",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"clean:build": "rimraf ./dist",
Expand All @@ -29,9 +29,7 @@
"typecheck": "tsc"
},
"dependencies": {
"@node-boot/context": "1.0.0"
},
"peerDependencies": {
"routing-controllers": ">=0.10.4"
"@node-boot/context": "1.0.0",
"@node-boot/engine": "1.0.0"
}
}
19 changes: 9 additions & 10 deletions packages/authorization/src/decorator/Authorized.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Authorized as InnerAuthorized} from "routing-controllers";
import {NodeBootToolkit} from "@node-boot/engine";

/**
* Marks controller action to have a special access.
Expand Down Expand Up @@ -27,15 +27,14 @@ export function Authorized(role: Function): Function;
/**
* Marks controller action to have a special access.
* Authorization logic must be defined in routing-controllers settings.
*
* @param roleOrRoles Arguments for routing-controllers @Authorized decorator
*/
export function Authorized(roleOrRoles?: string | string[] | Function) {
return <TFunction extends Function>(
clsOrObject: Function | Object,
method?: string,
) => {
// DI is optional and the decorator will only be applied if the DI container dependency is available.
InnerAuthorized(roleOrRoles)(clsOrObject, method);
export function Authorized(roleOrRoles?: string | string[] | Function): Function {
return function (clsOrObject: Function | Object, method?: string) {
NodeBootToolkit.getMetadataArgsStorage().responseHandlers.push({
type: "authorized",
target: method ? clsOrObject.constructor : (clsOrObject as Function),
method: method,
value: roleOrRoles,
});
};
}
17 changes: 11 additions & 6 deletions packages/authorization/src/decorator/CurrentUser.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import {CurrentUser as InnerCurrentUser} from "routing-controllers";
import {NodeBootToolkit} from "@node-boot/engine";

/**
* Injects currently authorized user.
* Authorization logic must be defined in routing-controllers settings.
*
* @param args Arguments for routing-controllers @CurrentUser decorator
*/
export function CurrentUser(...args: Parameters<typeof InnerCurrentUser>) {
return (target: Object, methodName: string, index: number) => {
InnerCurrentUser(...args)(target, methodName, index);
export function CurrentUser(options?: {required?: boolean}) {
return function (object: Object, methodName: string, index: number) {
NodeBootToolkit.getMetadataArgsStorage().params.push({
type: "current-user",
object: object,
method: methodName,
index: index,
parse: false,
required: options?.required ?? false,
});
};
}
33 changes: 10 additions & 23 deletions packages/authorization/src/decorator/EnableAuthorization.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,22 @@
import {ApplicationContext, RequestContext} from "@node-boot/context";
import {AuthorizationResolver, CurrentUserResolver} from "../resolver";
import {Action} from "routing-controllers/types/Action";
import {ApplicationContext, AuthorizationChecker, CurrentUserChecker} from "@node-boot/context";

/**
* Enable Authorization features by providing an Authorization and current user resolvers.
*
* @param CurrentUserResolverClass class implementing CurrentUserResolver interface
* @param AuthorizationResolverClass class implementing AuthorizationResolver interface
* @param CurrentUserCheckerClass class implementing CurrentUserResolver interface
* @param AuthorizationCheckerClass class implementing AuthorizationResolver interface
*/
export function EnableAuthorization(
CurrentUserResolverClass?: new () => CurrentUserResolver,
AuthorizationResolverClass?: new () => AuthorizationResolver,
CurrentUserCheckerClass?: new () => CurrentUserChecker,
AuthorizationCheckerClass?: new () => AuthorizationChecker,
): Function {
return function (object: Function) {
if (AuthorizationResolverClass) {
const authResolver = new AuthorizationResolverClass();
ApplicationContext.get().authorizationChecker = async (
context: RequestContext,
roles: any[],
) => {
return authResolver.authorize(context, roles);
};
return function () {
if (AuthorizationCheckerClass) {
ApplicationContext.get().authorizationChecker = new AuthorizationCheckerClass();
}

if (CurrentUserResolverClass) {
const userResolver = new CurrentUserResolverClass();
ApplicationContext.get().currentUserChecker = async (
action: Action,
) => {
return userResolver.getCurrentUser(action);
};
if (CurrentUserCheckerClass) {
ApplicationContext.get().currentUserChecker = new CurrentUserCheckerClass();
}
};
}
1 change: 0 additions & 1 deletion packages/authorization/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export * from "./decorator";
export * from "./resolver";
12 changes: 0 additions & 12 deletions packages/authorization/src/resolver/AuthorizationResolver.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/authorization/src/resolver/CurrentUserResolver.ts

This file was deleted.

2 changes: 0 additions & 2 deletions packages/authorization/src/resolver/index.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/config/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/** @type {import('jest').Config} */
module.exports = {
transform: {
"^.+\\.(t|j)sx?$": "@swc/jest",
Expand Down
2 changes: 1 addition & 1 deletion packages/config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"access": "public"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"types": "src/index.ts",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"clean:build": "rimraf ./dist",
Expand Down
28 changes: 6 additions & 22 deletions packages/config/src/decorator/ConfigurationProperties.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import {
ApplicationContext,
ConfigurationPropertiesAdapter,
IocContainer,
} from "@node-boot/context";
import {ApplicationContext, ConfigurationPropertiesAdapter, IocContainer} from "@node-boot/context";
import {ConfigurationPropertiesMetadata} from "../metadata";
import {ConfigService} from "../service";

export function ConfigurationProperties(
args: ConfigurationPropertiesMetadata,
): Function {
export function ConfigurationProperties(args: ConfigurationPropertiesMetadata): Function {
return function (target: any) {
Reflect.defineMetadata("config:isConfigProperties", true, target);
Reflect.defineMetadata("config:path", args.configPath, target);
Expand All @@ -17,22 +11,14 @@ export function ConfigurationProperties(
new (class implements ConfigurationPropertiesAdapter {
bind(iocContainer: IocContainer) {
const config: ConfigService = iocContainer.get("config");
const configProperties = config.get<typeof target>(
args.configPath,
);
const configProperties = config.get<typeof target>(args.configPath);

if (configProperties) {
const instance = new target();

for (const propertyName in configProperties) {
if (
Object.prototype.hasOwnProperty.call(
configProperties,
propertyName,
)
) {
instance[propertyName] =
configProperties[propertyName];
if (Object.prototype.hasOwnProperty.call(configProperties, propertyName)) {
instance[propertyName] = configProperties[propertyName];
}
}
if (!iocContainer.has(args.configName)) {
Expand All @@ -43,9 +29,7 @@ export function ConfigurationProperties(
);
}
} else {
throw new Error(
`Configuration for prefix '${args.configPath}' not found.`,
);
throw new Error(`Configuration for prefix '${args.configPath}' not found.`);
}
}
})(),
Expand Down
5 changes: 1 addition & 4 deletions packages/config/src/service/ConfigService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ export class ConfigService implements Config {

private readonly subscribers: (() => void)[] = [];

constructor(
private readonly parent?: ConfigService,
private parentKey?: string,
) {
constructor(private readonly parent?: ConfigService, private parentKey?: string) {
if (parent && !parentKey) {
throw new Error("parentKey is required if parent is set");
}
Expand Down
12 changes: 3 additions & 9 deletions packages/config/src/service/ObservableConfigProxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,9 @@ describe("ObservableConfigProxy", () => {
expect(config3.getNumber("x")).toBe(6);

config1.setConfig(new ConfigReader({}));
expect(() => config1.getNumber("x")).toThrow(
"Missing required config value at 'x'",
);
expect(() => config2.getNumber("x")).toThrow(
"Missing required config value at 'a'",
);
expect(() => config3.getNumber("x")).toThrow(
"Missing required config value at 'a'",
);
expect(() => config1.getNumber("x")).toThrow("Missing required config value at 'x'");
expect(() => config2.getNumber("x")).toThrow("Missing required config value at 'a'");
expect(() => config3.getNumber("x")).toThrow("Missing required config value at 'a'");

config1.setConfig(new ConfigReader({x: "s", a: {x: "s", b: {x: "s"}}}));
expect(() => config1.getNumber("x")).toThrow(
Expand Down
16 changes: 3 additions & 13 deletions packages/config/src/service/config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import {resolve as resolvePath} from "path";
import parseArgs from "minimist";
import {findPaths} from "@backstage/cli-common";
import {
ConfigTarget,
loadConfig,
LoadConfigOptionsRemote,
} from "@backstage/config-loader";
import {ConfigTarget, loadConfig, LoadConfigOptionsRemote} from "@backstage/config-loader";
import type {AppConfig} from "@backstage/config";
import {ConfigReader} from "@backstage/config";
import {ConfigService} from "./ConfigService";
Expand Down Expand Up @@ -51,11 +47,7 @@ export async function loadNodeBootConfig(options: {
remote: options.remote,
watch: {
onChange(newConfigs) {
console.info(
`Reloaded config from ${newConfigs
.map(c => c.context)
.join(", ")}`,
);
console.info(`Reloaded config from ${newConfigs.map(c => c.context).join(", ")}`);
const configsToMerge = [...newConfigs];
if (options.additionalConfigs) {
configsToMerge.push(...options.additionalConfigs);
Expand All @@ -71,9 +63,7 @@ export async function loadNodeBootConfig(options: {
},
});

console.info(
`Loaded config from ${appConfigs.map(c => c.context).join(", ")}`,
);
console.info(`Loaded config from ${appConfigs.map(c => c.context).join(", ")}`);

const finalAppConfigs = [...appConfigs];
if (options.additionalConfigs) {
Expand Down
1 change: 0 additions & 1 deletion packages/context/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/** @type {import('jest').Config} */
module.exports = {
transform: {
"^.+\\.(t|j)sx?$": "@swc/jest",
Expand Down
Loading