Skip to content

Commit

Permalink
feat(graphql): Add keyset connections
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-martin committed Sep 15, 2020
1 parent 0ecca6e commit 36bdbdd
Show file tree
Hide file tree
Showing 29 changed files with 1,054 additions and 421 deletions.
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
},
"peerDependencies": {
"@nestjs/common": "^7.0.0",
"class-transformer": "^0.2.3 || ^0.3.0"
"class-transformer": "^0.2.3 || ^0.3.0",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"@nestjs/common": "7.4.4",
Expand Down
24 changes: 14 additions & 10 deletions packages/core/src/common/reflect.utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'reflect-metadata';
import { Class } from './class.type';

export type MetaValue<MetaType> = MetaType | undefined;
Expand Down Expand Up @@ -25,23 +26,26 @@ abstract class Reflector {
constructor(readonly metaKey: string) {}

// eslint-disable-next-line @typescript-eslint/ban-types
protected getMetadata<Data>(target: Function): MetaValue<Data> {
protected getMetadata<Data>(target: Function, includeParents: boolean): MetaValue<Data> {
if (includeParents) {
return Reflect.getMetadata(this.metaKey, target) as MetaValue<Data>;
}
return Reflect.getOwnMetadata(this.metaKey, target) as MetaValue<Data>;
}

// eslint-disable-next-line @typescript-eslint/ban-types
protected defineMetadata = <Data>(data: Data, target: Function): void => {
protected defineMetadata<Data>(data: Data, target: Function): void {
Reflect.defineMetadata(this.metaKey, data, target);
};
}
}

export class ValueReflector extends Reflector {
set<DTO, Data>(DTOClass: Class<DTO>, data: Data): void {
this.defineMetadata(data, DTOClass);
}

get<DTO, Data>(DTOClass: Class<DTO>): MetaValue<Data> {
return this.getMetadata(DTOClass);
get<DTO, Data>(DTOClass: Class<DTO>, includeParents = false): MetaValue<Data> {
return this.getMetadata(DTOClass, includeParents);
}

isDefined<DTO>(DTOClass: Class<DTO>): boolean {
Expand All @@ -66,8 +70,8 @@ export class ArrayReflector extends Reflector {
this.defineMetadata(metadata, DTOClass);
}

get<DTO, Data>(DTOClass: Class<DTO>): MetaValue<Data[]> {
return this.getMetadata(DTOClass);
get<DTO, Data>(DTOClass: Class<DTO>, includeParents = false): MetaValue<Data[]> {
return this.getMetadata(DTOClass, includeParents);
}
}

Expand All @@ -78,12 +82,12 @@ export class MapReflector<K = string> extends Reflector {
this.defineMetadata(metadata, DTOClass);
}

get<DTO, Data>(DTOClass: Class<DTO>, key: K): MetaValue<Data> {
return this.getMetadata<Map<K, Data>>(DTOClass)?.get(key);
get<DTO, Data>(DTOClass: Class<DTO>, key: K, includeParents = false): MetaValue<Data> {
return this.getMetadata<Map<K, Data>>(DTOClass, includeParents)?.get(key);
}

has<DTO>(DTOClass: Class<DTO>, key: K): boolean {
return this.getMetadata<Map<K, unknown>>(DTOClass)?.has(key) ?? false;
return this.getMetadata<Map<K, unknown>>(DTOClass, false)?.has(key) ?? false;
}

memoize<DTO, Data>(DTOClass: Class<DTO>, key: K, fn: () => Data): Data {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export {
applySort,
applyPaging,
applyQuery,
invertSort,
} from './query.helpers';
export { transformAggregateQuery, transformAggregateResponse } from './aggregate.helpers';
23 changes: 21 additions & 2 deletions packages/core/src/helpers/query.helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import merge from 'lodash.merge';
import { Filter, Paging, Query, SortField } from '../interfaces';
import { Filter, Paging, Query, SortDirection, SortField, SortNulls } from '../interfaces';
import { FilterBuilder } from './filter.builder';
import { SortBuilder } from './sort.builder';
import { PageBuilder } from './page.builder';
Expand Down Expand Up @@ -53,7 +53,13 @@ export const transformQuery = <From, To>(query: Query<From>, fieldMap: QueryFiel
};

export const mergeFilter = <T>(base: Filter<T>, source: Filter<T>): Filter<T> => {
return merge(base, source);
if (!Object.keys(base).length) {
return source;
}
if (!Object.keys(source).length) {
return base;
}
return { and: [source, base] } as Filter<T>;
};

export const mergeQuery = <T>(base: Query<T>, source: Query<T>): Query<T> => {
Expand Down Expand Up @@ -100,3 +106,16 @@ export const applyQuery = <DTO>(dtos: DTO[], query: Query<DTO>): DTO[] => {
const sorted = applySort(filtered, query.sorting ?? []);
return applyPaging(sorted, query.paging ?? {});
};

export function invertSort<DTO>(sortFields: SortField<DTO>[]): SortField<DTO>[] {
return sortFields.map((sf) => {
const direction = sf.direction === SortDirection.ASC ? SortDirection.DESC : SortDirection.ASC;
let nulls;
if (sf.nulls === SortNulls.NULLS_LAST) {
nulls = SortNulls.NULLS_FIRST;
} else if (sf.nulls === SortNulls.NULLS_FIRST) {
nulls = SortNulls.NULLS_LAST;
}
return { ...sf, direction, nulls };
});
}
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
applyQuery,
mergeQuery,
mergeFilter,
invertSort,
} from './helpers';
export {
ClassTransformerAssembler,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
type Test {
stringField: String!
numberField: Float!
boolField: Boolean!
}

type TestTotalCount {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
type Test {
stringField: String!
numberField: Float!
boolField: Boolean!
}

type TestEdge {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ describe('DeleteResolver', () => {
const context = {};
const authorizeFilter: Filter<TestResolverDTO> = { stringField: { eq: 'foo' } };
when(mockAuthorizer.authorize(context)).thenResolve(authorizeFilter);
when(mockService.deleteMany(objectContaining({ ...input.filter, ...authorizeFilter }))).thenResolve(output);
when(mockService.deleteMany(objectContaining({ and: [authorizeFilter, input.filter] }))).thenResolve(output);
const result = await resolver.deleteMany({ input }, context);
return expect(result).toEqual(output);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ describe('UpdateResolver', () => {
const authorizeFilter: Filter<TestResolverDTO> = { stringField: { eq: 'foo' } };
when(mockAuthorizer.authorize(context)).thenResolve(authorizeFilter);
when(
mockService.updateMany(objectContaining(input.input.update), objectContaining(input.input.filter)),
mockService.updateMany(
objectContaining(input.input.update),
objectContaining({ and: [authorizeFilter, input.input.filter] }),
),
).thenResolve(output);
const result = await resolver.updateMany(input, context);
return expect(result).toEqual(output);
Expand Down
Loading

0 comments on commit 36bdbdd

Please sign in to comment.