From 24429afb9f44ba3bf22eb606ea7e4363359a7f59 Mon Sep 17 00:00:00 2001 From: Roee Shachar Date: Tue, 27 Apr 2021 14:04:21 +0300 Subject: [PATCH] feat!: add graphql class decorators (#58) * feat: add graphql class decorators * fix: linting, prettier * feat: add enum support * fix: linting, prettier * feat: new ts types * fix: lint for naming convention --- .../property/classGraphql.decorator.ts | 50 +++++++++++++++++ .../decorators/property/graphql.decorator.ts | 16 ++++++ src/models/common/index.ts | 6 ++ .../common/interfaces/link.interface.ts | 6 -- .../propGraphQLMapping.interface.ts | 6 ++ src/models/index.ts | 1 + .../property/catalogDB.decorator.ts | 4 +- .../decorators/property/tsTypes.decorator.ts | 56 ++++++++++++++++--- src/models/layerMetadata/index.ts | 2 +- src/models/layerMetadata/layerMetadata.ts | 39 ++++++++++++- src/models/layerMetadata/link.ts | 34 +++++++++++ .../layerMetadata/pycswLayerCatalogRecord.ts | 50 ++++++++++++++++- src/models/pycsw/interfaces/pycswCoreModel.ts | 2 +- 13 files changed, 252 insertions(+), 20 deletions(-) create mode 100644 src/models/common/decorators/property/classGraphql.decorator.ts create mode 100644 src/models/common/decorators/property/graphql.decorator.ts create mode 100644 src/models/common/index.ts delete mode 100644 src/models/common/interfaces/link.interface.ts create mode 100644 src/models/common/interfaces/propGraphQLMapping.interface.ts create mode 100644 src/models/layerMetadata/link.ts diff --git a/src/models/common/decorators/property/classGraphql.decorator.ts b/src/models/common/decorators/property/classGraphql.decorator.ts new file mode 100644 index 0000000..f90fb5b --- /dev/null +++ b/src/models/common/decorators/property/classGraphql.decorator.ts @@ -0,0 +1,50 @@ +import 'reflect-metadata'; +import { getTsTypesMapping } from '../../../layerMetadata/decorators/property/tsTypes.decorator'; +import { IPropGraphQLMapping } from '../../interfaces/propGraphQLMapping.interface'; +import { getGraphQLMapping } from './graphql.decorator'; + +const graphQLMetadataKey = Symbol('graphqlclassmapping'); +type KeyValueDict = Record; +const target = {}; + +export interface IGraphQLClassMapping { + name: string; + fields: IPropGraphQLMapping[]; +} + +export function graphqlClass(): ClassDecorator { + // eslint-disable-next-line @typescript-eslint/ban-types + return (graphqlmapping: TFunction) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + const classInstance = new (graphqlmapping as any)(); + const classData: IGraphQLClassMapping = { + fields: getGraphQLMappings(classInstance), + name: graphqlmapping.name, + }; + + const classDataList = getGraphQLClassMapping() ?? []; + classDataList.push(classData); + Reflect.defineMetadata(graphQLMetadataKey, classDataList, target); + return graphqlmapping; + }; +} + +export function getGraphQLClassMapping(): IGraphQLClassMapping[] | undefined { + return Reflect.getMetadata(graphQLMetadataKey, target) as IGraphQLClassMapping[]; +} + +export function getGraphQLMappings(object: KeyValueDict): IPropGraphQLMapping[] { + const ret = []; + for (const prop in object) { + const graphQLMap = getGraphQLMapping(object, prop); + const tsTypesMap = getTsTypesMapping(object, prop); + if (graphQLMap && tsTypesMap) { + ret.push({ + prop: prop, + ...graphQLMap, + ...tsTypesMap, + }); + } + } + return ret; +} diff --git a/src/models/common/decorators/property/graphql.decorator.ts b/src/models/common/decorators/property/graphql.decorator.ts new file mode 100644 index 0000000..42a077d --- /dev/null +++ b/src/models/common/decorators/property/graphql.decorator.ts @@ -0,0 +1,16 @@ +import 'reflect-metadata'; + +const graphQLMetadataKey = Symbol('graphqlmapping'); + +export interface IGraphQLMapping { + nullable?: boolean; +} + +export function graphql(graphqlmapping?: IGraphQLMapping): PropertyDecorator { + const defaultMapping: IGraphQLMapping = { nullable: false }; + return Reflect.metadata(graphQLMetadataKey, graphqlmapping ?? defaultMapping); +} + +export function getGraphQLMapping(target: T, propertyKey: string): IGraphQLMapping | undefined { + return Reflect.getMetadata(graphQLMetadataKey, target, propertyKey) as IGraphQLMapping; +} diff --git a/src/models/common/index.ts b/src/models/common/index.ts new file mode 100644 index 0000000..464dcc5 --- /dev/null +++ b/src/models/common/index.ts @@ -0,0 +1,6 @@ +export * from './decorators/property/graphql.decorator'; +export * from './decorators/property/classGraphql.decorator'; +export * from './interfaces/ormCatalog.interface'; +export * from './interfaces/propCatalogDBMapping.interface'; +export * from './interfaces/propGraphQLMapping.interface'; +export * from '../layerMetadata/link'; diff --git a/src/models/common/interfaces/link.interface.ts b/src/models/common/interfaces/link.interface.ts deleted file mode 100644 index edbbc38..0000000 --- a/src/models/common/interfaces/link.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Link { - name?: string; - description?: string; - protocol: string; - url: string; -} diff --git a/src/models/common/interfaces/propGraphQLMapping.interface.ts b/src/models/common/interfaces/propGraphQLMapping.interface.ts new file mode 100644 index 0000000..1626cbc --- /dev/null +++ b/src/models/common/interfaces/propGraphQLMapping.interface.ts @@ -0,0 +1,6 @@ +import { IGraphQLMapping } from '../decorators/property/graphql.decorator'; +import { ITsTypesMapping } from '../../layerMetadata/decorators/property/tsTypes.decorator'; + +export interface IPropGraphQLMapping extends IGraphQLMapping, ITsTypesMapping { + prop: string; +} diff --git a/src/models/index.ts b/src/models/index.ts index e4ee891..987c34d 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -1,3 +1,4 @@ +export * from './common/index'; export * from './layerMetadata/index'; export * from './discreteIngestion/index'; export * from './layerMetadata/pycswLayerCatalogRecord'; diff --git a/src/models/layerMetadata/decorators/property/catalogDB.decorator.ts b/src/models/layerMetadata/decorators/property/catalogDB.decorator.ts index 2bb2f10..fab5764 100644 --- a/src/models/layerMetadata/decorators/property/catalogDB.decorator.ts +++ b/src/models/layerMetadata/decorators/property/catalogDB.decorator.ts @@ -1,5 +1,5 @@ import 'reflect-metadata'; -import { TsTypes } from './tsTypes.decorator'; +import { IDescribeTsType } from './tsTypes.decorator'; const catalogDbMetadataKey = Symbol('catalogdbmapping'); @@ -15,7 +15,7 @@ export interface IColumnProps { } export interface IFieldProps { - overrideType?: TsTypes; + overrideType?: IDescribeTsType; } export interface ICatalogDBMapping { diff --git a/src/models/layerMetadata/decorators/property/tsTypes.decorator.ts b/src/models/layerMetadata/decorators/property/tsTypes.decorator.ts index c7f89d9..51dbc5e 100644 --- a/src/models/layerMetadata/decorators/property/tsTypes.decorator.ts +++ b/src/models/layerMetadata/decorators/property/tsTypes.decorator.ts @@ -2,18 +2,58 @@ import 'reflect-metadata'; const tsTypesMetadataKey = Symbol('catalogdbmapping'); -export enum TsTypes { - STRING = 'string', - BOOLEAN = 'boolean', - DATE = 'Date', - NUMBER = 'number', +export enum PropertiesTypes { + PRIMITIVE = 'primitive', + ENUM = 'enum', + CLASS = 'class', + ARRAY = 'array', OBJECT = 'object', - LINK = 'Link', - LINKS = 'Link[]', +} +export interface IDescribeTsType { + value: string; + type: PropertiesTypes; + importFromPackage?: string; } +/* eslint-disable @typescript-eslint/naming-convention */ +export const TsTypes: Record = { + STRING: { + value: 'string', + type: PropertiesTypes.PRIMITIVE, + }, + BOOLEAN: { + value: 'boolean', + type: PropertiesTypes.PRIMITIVE, + }, + DATE: { + value: 'Date', + type: PropertiesTypes.PRIMITIVE, + }, + NUMBER: { + value: 'number', + type: PropertiesTypes.PRIMITIVE, + }, + OBJECT: { + value: 'object', + type: PropertiesTypes.OBJECT, + }, + LINK: { + value: 'Link', + type: PropertiesTypes.CLASS, + }, + LINKS: { + value: 'Link', + type: PropertiesTypes.ARRAY, + }, + SENSORTYPE: { + value: 'SensorType', + type: PropertiesTypes.ENUM, + importFromPackage: '@map-colonies/mc-model-types', + }, +}; +/* eslint-enable @typescript-eslint/naming-convention */ export interface ITsTypesMapping { - mappingType: TsTypes; + mappingType: IDescribeTsType; } export function tsTypes(tsTypesMapping: ITsTypesMapping): PropertyDecorator { diff --git a/src/models/layerMetadata/index.ts b/src/models/layerMetadata/index.ts index 72e3b1d..5be1931 100644 --- a/src/models/layerMetadata/index.ts +++ b/src/models/layerMetadata/index.ts @@ -2,7 +2,7 @@ export * from './layerMetadata'; export * from './layerMetadata-StatusFields'; export { IShpMapping, ShapeFileType } from './decorators/property/shp.decorator'; -export { TsTypes } from './decorators/property/tsTypes.decorator'; +export { TsTypes, IDescribeTsType, PropertiesTypes } from './decorators/property/tsTypes.decorator'; export { IPYCSWMapping } from './decorators/property/csw.decorator'; export { IColumnProps } from './decorators/property/catalogDB.decorator'; export { IOrmCatalog } from '../common/interfaces/ormCatalog.interface'; diff --git a/src/models/layerMetadata/layerMetadata.ts b/src/models/layerMetadata/layerMetadata.ts index 7fef5b0..8847341 100644 --- a/src/models/layerMetadata/layerMetadata.ts +++ b/src/models/layerMetadata/layerMetadata.ts @@ -1,5 +1,6 @@ import { GeoJSON } from 'geojson'; import { IPropCatalogDBMapping } from '../common/interfaces/propCatalogDBMapping.interface'; +import { graphql } from '../common/decorators/property/graphql.decorator'; import { getPyCSWMapping, IPYCSWMapping, pycsw } from './decorators/property/csw.decorator'; import { getShpMapping, IShpMapping, ShapeFileType, shpMapping } from './decorators/property/shp.decorator'; import { getCatalogDBMapping, ICatalogDBMapping, catalogDB } from './decorators/property/catalogDB.decorator'; @@ -58,6 +59,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public source?: string = undefined; /** @@ -82,6 +86,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public sourceName?: string = undefined; /** @@ -106,6 +113,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.DATE, }) + @graphql({ + nullable: true, + }) public updateDate?: Date = undefined; /** @@ -130,6 +140,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.NUMBER, }) + @graphql({ + nullable: true, + }) public resolution?: number = undefined; /** @@ -155,6 +168,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.NUMBER, }) + @graphql({ + nullable: true, + }) public ep90?: number = undefined; /** @@ -177,7 +193,10 @@ export class LayerMetadata implements ILayerMetadata { valuePath: 'features[0].properties.SensorType', }) @tsTypes({ - mappingType: TsTypes.STRING, + mappingType: TsTypes.SENSORTYPE, + }) + @graphql({ + nullable: true, }) public sensorType?: SensorType = undefined; @@ -204,6 +223,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.NUMBER, }) + @graphql({ + nullable: true, + }) public rms?: number = undefined; /** @@ -229,6 +251,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public scale?: string = undefined; /** @@ -254,6 +279,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public dsc?: string = undefined; /** @@ -279,6 +307,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.OBJECT, }) + @graphql({ + nullable: true, + }) public geometry?: GeoJSON = undefined; /** @@ -300,6 +331,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public id?: string = undefined; /** @@ -321,6 +355,9 @@ export class LayerMetadata implements ILayerMetadata { @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public version?: string = undefined; public static getPyCSWMapping(prop: string): IPYCSWMapping | undefined { diff --git a/src/models/layerMetadata/link.ts b/src/models/layerMetadata/link.ts new file mode 100644 index 0000000..01167f1 --- /dev/null +++ b/src/models/layerMetadata/link.ts @@ -0,0 +1,34 @@ +import { graphqlClass } from '../common/decorators/property/classGraphql.decorator'; +import { graphql } from '../common/decorators/property/graphql.decorator'; +import { TsTypes, tsTypes } from './decorators/property/tsTypes.decorator'; + +@graphqlClass() +export class Link { + @tsTypes({ + mappingType: TsTypes.STRING, + }) + @graphql({ + nullable: true, + }) + public name?: string = undefined; + + @tsTypes({ + mappingType: TsTypes.STRING, + }) + @graphql({ + nullable: true, + }) + public description?: string = undefined; + + @tsTypes({ + mappingType: TsTypes.STRING, + }) + @graphql() + public protocol?: string = undefined; + + @tsTypes({ + mappingType: TsTypes.STRING, + }) + @graphql() + public url?: string = undefined; +} diff --git a/src/models/layerMetadata/pycswLayerCatalogRecord.ts b/src/models/layerMetadata/pycswLayerCatalogRecord.ts index 39a6b00..7386e93 100644 --- a/src/models/layerMetadata/pycswLayerCatalogRecord.ts +++ b/src/models/layerMetadata/pycswLayerCatalogRecord.ts @@ -1,7 +1,9 @@ import { IPycswCoreModel } from '../pycsw/interfaces/pycswCoreModel'; import { IPropCatalogDBMapping } from '../common/interfaces/propCatalogDBMapping.interface'; import { IOrmCatalog } from '../common/interfaces/ormCatalog.interface'; -import { Link } from '../common/interfaces/link.interface'; +import { graphql } from '../common/decorators/property/graphql.decorator'; +import { graphqlClass } from '../common/decorators/property/classGraphql.decorator'; +import { Link } from './link'; import { catalogDB, getCatalogDBMapping } from './decorators/property/catalogDB.decorator'; import { getTsTypesMapping, TsTypes, tsTypes } from './decorators/property/tsTypes.decorator'; import { LayerMetadata } from './layerMetadata'; @@ -10,6 +12,7 @@ import { getCatalogDBEntityMapping, catalogDBEntity, ICatalogDBEntityMapping } f @catalogDBEntity({ table: 'records', }) +@graphqlClass() export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCoreModel, IOrmCatalog { @catalogDB({ column: { @@ -20,6 +23,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public typeName?: string = undefined; @catalogDB({ @@ -31,6 +37,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public schema?: string = undefined; @catalogDB({ @@ -42,6 +51,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public mdSource?: string = undefined; @catalogDB({ @@ -53,6 +65,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public xml?: string = undefined; @catalogDB({ @@ -64,6 +79,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public anyText?: string = undefined; @catalogDB({ @@ -75,6 +93,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.DATE, }) + @graphql({ + nullable: true, + }) public insertDate?: Date = undefined; @catalogDB({ @@ -87,6 +108,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public wktGeometry?: string = undefined; @catalogDB({ @@ -102,6 +126,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.LINKS, }) + @graphql({ + nullable: true, + }) public links?: Link[] = undefined; @catalogDB({ @@ -114,6 +141,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public anyTextTsvector?: string = undefined; @catalogDB({ @@ -128,6 +158,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public wkbGeometry?: string = undefined; @catalogDB({ @@ -140,6 +173,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public title?: string = undefined; @catalogDB({ @@ -152,6 +188,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public type?: string = undefined; @catalogDB({ @@ -164,6 +203,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public srs?: string = undefined; @catalogDB({ @@ -177,6 +219,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public producerName?: string = undefined; @catalogDB({ @@ -189,6 +234,9 @@ export class PycswLayerCatalogRecord extends LayerMetadata implements IPycswCore @tsTypes({ mappingType: TsTypes.STRING, }) + @graphql({ + nullable: true, + }) public projectName?: string = undefined; public constructor() { diff --git a/src/models/pycsw/interfaces/pycswCoreModel.ts b/src/models/pycsw/interfaces/pycswCoreModel.ts index d85d511..a60fc06 100644 --- a/src/models/pycsw/interfaces/pycswCoreModel.ts +++ b/src/models/pycsw/interfaces/pycswCoreModel.ts @@ -1,4 +1,4 @@ -import { Link } from '../../common/interfaces/link.interface'; +import { Link } from '../../layerMetadata/link'; export interface IPycswCoreModel { typeName?: string;