Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Add Extensions metadata Decorator #1

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/decorators/Extensions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { MethodAndPropDecorator } from "./types";
import { SymbolKeysNotSupportedError } from "../errors";
import { getMetadataStorage } from "../metadata/getMetadataStorage";

export function Extensions(
extensions: Record<string, any>,
): MethodAndPropDecorator & ClassDecorator;
export function Extensions(
extensions: Record<string, any>,
): MethodDecorator | PropertyDecorator | ClassDecorator {
return (targetOrPrototype, propertyKey, descriptor) => {
if (typeof propertyKey === "symbol") {
throw new SymbolKeysNotSupportedError();
}
if (propertyKey) {
getMetadataStorage().collectExtensionsFieldMetadata({
target: targetOrPrototype.constructor,
fieldName: propertyKey,
extensions,
});
} else {
getMetadataStorage().collectExtensionsClassMetadata({
target: targetOrPrototype as Function,
extensions,
});
}
};
}
1 change: 1 addition & 0 deletions src/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export { createParamDecorator } from "./createParamDecorator";
export { createMethodDecorator } from "./createMethodDecorator";
export { Ctx } from "./Ctx";
export { Directive } from "./Directive";
export { Extensions } from "./Extensions";
export { registerEnumType } from "./enums";
export { Field, FieldOptions } from "./Field";
export { FieldResolver } from "./FieldResolver";
Expand Down
1 change: 1 addition & 0 deletions src/metadata/definitions/class-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export interface ClassMetadata {
description?: string;
isAbstract?: boolean;
directives?: DirectiveMetadata[];
extensions?: Record<string, any>;
simpleResolvers?: boolean;
}
10 changes: 10 additions & 0 deletions src/metadata/definitions/extensions-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface ExtensionsClassMetadata {
target: Function;
extensions: Record<string, any>;
}

export interface ExtensionsFieldMetadata {
target: Function;
fieldName: string;
extensions: Record<string, any>;
}
1 change: 1 addition & 0 deletions src/metadata/definitions/field-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ export interface FieldMetadata {
roles?: any[];
middlewares?: Array<Middleware<any>>;
directives?: DirectiveMetadata[];
extensions?: Record<string, any>;
simple?: boolean;
}
1 change: 1 addition & 0 deletions src/metadata/definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from "./authorized-metadata";
export * from "./class-metadata";
export * from "./directive-metadata";
export * from "./enum-metadata";
export * from "./extensions-metadata";
export * from "./field-metadata";
export * from "./middleware-metadata";
export * from "./param-metadata";
Expand Down
1 change: 1 addition & 0 deletions src/metadata/definitions/resolver-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface BaseResolverMetadata {
roles?: any[];
middlewares?: Array<Middleware<any>>;
directives?: DirectiveMetadata[];
extensions?: Record<string, any>;
}

export interface ResolverMetadata extends BaseResolverMetadata {
Expand Down
35 changes: 35 additions & 0 deletions src/metadata/metadata-storage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
ResolverMetadata,
ClassMetadata,
ExtensionsClassMetadata,
ExtensionsFieldMetadata,
FieldMetadata,
ParamMetadata,
FieldResolverMetadata,
Expand All @@ -20,6 +22,7 @@ import {
mapMiddlewareMetadataToArray,
mapSuperFieldResolverHandlers,
ensureReflectMetadataExists,
flattenExtensions,
} from "./utils";
import { ObjectClassMetadata } from "./definitions/object-class-metdata";
import { InterfaceClassMetadata } from "./definitions/interface-class-metadata";
Expand All @@ -40,6 +43,8 @@ export class MetadataStorage {
middlewares: MiddlewareMetadata[] = [];
classDirectives: DirectiveClassMetadata[] = [];
fieldDirectives: DirectiveFieldMetadata[] = [];
classExtensions: ExtensionsClassMetadata[] = [];
fieldExtensions: ExtensionsFieldMetadata[] = [];

private resolverClasses: ResolverClassMetadata[] = [];
private fields: FieldMetadata[] = [];
Expand Down Expand Up @@ -108,11 +113,20 @@ export class MetadataStorage {
this.fieldDirectives.push(definition);
}

collectExtensionsClassMetadata(definition: ExtensionsClassMetadata) {
this.classExtensions.push(definition);
}
collectExtensionsFieldMetadata(definition: ExtensionsFieldMetadata) {
this.fieldExtensions.push(definition);
}

build() {
// TODO: disable next build attempts

this.classDirectives.reverse();
this.fieldDirectives.reverse();
this.classExtensions.reverse();
Copy link
Author

@hihuz hihuz Jan 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary or else the first decorator declared will have priority, e.g.:

@ObjectType
@Extensions({ duplicate: "one" })
@Extensions({ duplicate: "two" })

would end up with the object having duplicate: "one", it seems counter intuitive to me and I think this is why it was done in the "directives" feature in the same way. I would have added a code comment but since there was none in the directives PR, I'm not sure it would be appreciated. Will ask the question to the maintainer

this.fieldExtensions.reverse();

this.buildClassMetadata(this.objectTypes);
this.buildClassMetadata(this.inputTypes);
Expand Down Expand Up @@ -143,6 +157,8 @@ export class MetadataStorage {
this.middlewares = [];
this.classDirectives = [];
this.fieldDirectives = [];
this.classExtensions = [];
this.fieldExtensions = [];

this.resolverClasses = [];
this.fields = [];
Expand All @@ -167,6 +183,7 @@ export class MetadataStorage {
field.directives = this.fieldDirectives
.filter(it => it.target === field.target && it.fieldName === field.name)
.map(it => it.directive);
field.extensions = this.findFieldExtensions(field.target, field.name);
});
def.fields = fields;
}
Expand All @@ -175,6 +192,9 @@ export class MetadataStorage {
.filter(it => it.target === def.target)
.map(it => it.directive);
}
if (!def.extensions) {
def.extensions = this.findClassExtensions(def.target);
}
});
}

Expand All @@ -196,6 +216,7 @@ export class MetadataStorage {
def.directives = this.fieldDirectives
.filter(it => it.target === def.target && it.fieldName === def.methodName)
.map(it => it.directive);
def.extensions = this.findFieldExtensions(def.target, def.methodName);
});
}

Expand All @@ -206,6 +227,7 @@ export class MetadataStorage {
def.directives = this.fieldDirectives
.filter(it => it.target === def.target && it.fieldName === def.methodName)
.map(it => it.directive);
def.extensions = this.findFieldExtensions(def.target, def.methodName);
def.getObjectType =
def.kind === "external"
? this.resolverClasses.find(resolver => resolver.target === def.target)!.getObjectType
Expand Down Expand Up @@ -236,6 +258,7 @@ export class MetadataStorage {
middlewares: def.middlewares!,
params: def.params!,
directives: def.directives,
extensions: def.extensions,
};
this.collectClassFieldMetadata(fieldMetadata);
objectType.fields!.push(fieldMetadata);
Expand Down Expand Up @@ -286,4 +309,16 @@ export class MetadataStorage {
}
return authorizedField.roles;
}

private findClassExtensions(target: Function): Record<string, any> {
return this.classExtensions
.filter(entry => entry.target === target)
.reduce(flattenExtensions, {});
}

private findFieldExtensions(target: Function, fieldName: string): Record<string, any> {
return this.fieldExtensions
.filter(entry => entry.target === target && entry.fieldName === fieldName)
.reduce(flattenExtensions, {});
}
}
9 changes: 9 additions & 0 deletions src/metadata/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
BaseResolverMetadata,
MiddlewareMetadata,
FieldResolverMetadata,
ExtensionsClassMetadata,
ExtensionsFieldMetadata,
} from "./definitions";
import { Middleware } from "../interfaces/Middleware";
import { isThrowing } from "../helpers/isThrowing";
Expand Down Expand Up @@ -57,3 +59,10 @@ export function ensureReflectMetadataExists() {
throw new ReflectMetadataMissingError();
}
}

export function flattenExtensions(
Copy link
Author

@hihuz hihuz Jan 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not such a fan of having a "util" just for this but I didn't see a proper alternative.

I could make it more generic by accepting a key by which we would flatten instead of hardcoding extensions, but since there is no other use through the codebase now it looked like premature abstraction to me

extensions: Record<string, any>,
entry: ExtensionsClassMetadata | ExtensionsFieldMetadata,
): Record<string, any> {
return { ...extensions, ...entry.extensions };
}
5 changes: 5 additions & 0 deletions src/schema/schema-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ export abstract class SchemaGenerator {
name: objectType.name,
description: objectType.description,
astNode: getObjectTypeDefinitionNode(objectType.name, objectType.directives),
extensions: objectType.extensions,
interfaces: () => {
let interfaces = interfaceClasses.map<GraphQLInterfaceType>(
interfaceClass =>
Expand Down Expand Up @@ -331,6 +332,7 @@ export abstract class SchemaGenerator {
astNode: getFieldDefinitionNode(field.name, type, field.directives),
extensions: {
complexity: field.complexity,
...field.extensions,
},
};
return fieldsMap;
Expand Down Expand Up @@ -379,6 +381,7 @@ export abstract class SchemaGenerator {
type: new GraphQLInputObjectType({
name: inputType.name,
description: inputType.description,
extensions: inputType.extensions,
fields: () => {
let fields = inputType.fields!.reduce<GraphQLInputFieldConfigMap>(
(fieldsMap, field) => {
Expand All @@ -399,6 +402,7 @@ export abstract class SchemaGenerator {
type,
defaultValue: field.typeOptions.defaultValue,
astNode: getInputValueDefinitionNode(field.name, type, field.directives),
extensions: field.extensions,
};
return fieldsMap;
},
Expand Down Expand Up @@ -498,6 +502,7 @@ export abstract class SchemaGenerator {
astNode: getFieldDefinitionNode(handler.schemaName, type, handler.directives),
extensions: {
complexity: handler.complexity,
...handler.extensions,
},
};
return fields;
Expand Down
Loading