Skip to content

Commit

Permalink
feat: merge with graphql extension
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jan 12, 2021
1 parent ca34654 commit cea8161
Show file tree
Hide file tree
Showing 29 changed files with 278 additions and 42 deletions.
1 change: 1 addition & 0 deletions libs/ngrx-entity-relationship/augments/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
8 changes: 8 additions & 0 deletions libs/ngrx-entity-relationship/augments/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare global {
// tslint:disable-next-line:class-name
export interface SELECTOR_META {
gqlFields?: Array<string>;
}
}

export {};
1 change: 1 addition & 0 deletions libs/ngrx-entity-relationship/graphql/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
160 changes: 160 additions & 0 deletions libs/ngrx-entity-relationship/graphql/src/lib/toGraphQL.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import {ENTITY_SELECTOR} from 'ngrx-entity-relationship';
import 'ngrx-entity-relationship/augments';

const resolveGraphQL = (
selector: ENTITY_SELECTOR,
options: {
include: Array<keyof any>;
prefix: string;
level: number;
} = {
include: [],
prefix: '',
level: 0,
},
) => {
const prefix = options.prefix ? options.prefix.repeat(options.level) : '';
let query = '';
const included: Array<string> = [];
for (const relationship of selector.relationships) {
if (typeof relationship.keyId !== 'string') {
continue;
}
if (typeof relationship.keyValue !== 'string') {
continue;
}
if (relationship.ngrxEntityRelationship === 'childrenEntities') {
included.push(relationship.keyValue);
query += `${prefix}${relationship.keyValue}`;
query += `${prefix ? ' ' : ''}{${prefix ? '\n' : ''}${resolveGraphQL(relationship, {
include: [relationship.keyId],
prefix: options.prefix,
level: options.level + 1,
})}${prefix}}${prefix ? '\n' : ''}`;
} else if (relationship.ngrxEntityRelationship === 'childEntity') {
included.push(relationship.keyValue);
query += `${prefix}${relationship.keyValue}`;
query += `${prefix ? ' ' : ''}{${prefix ? '\n' : ''}${resolveGraphQL(relationship, {
include: [relationship.keyId],
prefix: options.prefix,
level: options.level + 1,
})}${prefix}}${prefix ? '\n' : ''}`;
} else {
included.push(relationship.keyId);
included.push(relationship.keyValue);
query += `${prefix}${relationship.keyId}`;
query += `\n${prefix}${relationship.keyValue}`;
query += `${prefix ? ' ' : ''}{${prefix ? '\n' : ''}${resolveGraphQL(relationship, {
include: [],
prefix: options.prefix,
level: options.level + 1,
})}${prefix}}${prefix ? '\n' : ''}`;
}
}
for (const field of options.include) {
if (typeof field !== 'string') {
continue;
}
if (included.indexOf(field) !== -1) {
continue;
}
query += `${prefix}${field}\n`;
}
const fields = selector.meta.gqlFields || [];
for (const field of fields) {
if (included.indexOf(field) !== -1) {
continue;
}
query += `${prefix}${field}\n`;
}
return query;
};

function encodeValue(data: any): string | undefined {
if (typeof data === 'number') {
return JSON.stringify(data);
}
if (typeof data === 'string') {
return JSON.stringify(data);
}
if (data === null || data === undefined) {
return JSON.stringify(null);
}
if (typeof data === 'function') {
return undefined;
}
if (Array.isArray(data)) {
return JSON.stringify(data.map(encodeValue).filter(v => v !== undefined));
}
const fields: Array<string> = [];
for (const key of Object.keys(data)) {
const value = encodeValue(data[key]);
if (value === undefined) {
continue;
}
fields.push(`${key}:${value}`);
}
return `{${fields.join(',')}}`;
}

export function toGraphQL(...queries: Array<string>): string;
export function toGraphQL(query: string, params: object, selector: ENTITY_SELECTOR): string;
export function toGraphQL(query: string, selector: ENTITY_SELECTOR): string;

export function toGraphQL(...queries: Array<any>): string {
const prefix = '';
let query = '';
let selector: ENTITY_SELECTOR | undefined;
let params: Record<string, any> | null | string | undefined;
if (queries.length >= 2 && typeof queries[1] !== 'string') {
if (queries[2] && typeof queries[2] !== 'string') {
[query, params, selector] = queries;
} else {
[query, selector] = queries;
}
}

const stringParams: Array<string> = [];
if (typeof params === 'string') {
stringParams.push(params);
}
if (params && typeof params === 'object') {
for (const key of Object.keys(params)) {
if (key === '$') {
continue;
}
stringParams.push(`${key}:${prefix ? ' ' : ''}${encodeValue(params[key])}`);
}
}
if (params && typeof params === 'object' && params.$ && typeof params.$ === 'object') {
const params$ = params.$;
for (const key of Object.keys(params$)) {
if (typeof params$[key] !== 'string' || params$[key][0] !== '$') {
throw new Error(`${key} should be a variable that starts with $ symbol`);
}
stringParams.push(`${key}:${prefix ? ' ' : ''}${params$[key]}`);
}
}
params = stringParams.length ? `(${stringParams.join(`,${prefix ? ' ' : ''}`)})` : '';

if (selector) {
return `{\n${prefix}${query}${params}${prefix ? ' ' : ''}{\n${resolveGraphQL(selector, {
include: [],
prefix: `${prefix}`,
level: 2,
})}${prefix}}\n}`;
}
const parts: Array<string> = [];
for (let part of queries) {
if (typeof part !== 'string') {
continue;
}
part = part.trim();
if (part.substr(0, 1) === '{' && part.substr(part.length - 1, 1) === '}') {
part = part.substr(1, part.length - 2);
}
parts.push(part.trim());
}

return `{${parts.join('\n')}}`;
}
12 changes: 12 additions & 0 deletions libs/ngrx-entity-relationship/graphql/src/lib/toMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {toTerm} from './toTerm';

export function toMutation(toGraphQL: string): string;
export function toMutation(
variables: {
[key: string]: 'ID' | 'ID!' | 'String!' | 'String' | 'Boolean!' | 'Boolean' | string;
},
toGraphQL: string,
): string;
export function toMutation(...args: Array<any>): string {
return toTerm('mutation', ...args);
}
12 changes: 12 additions & 0 deletions libs/ngrx-entity-relationship/graphql/src/lib/toQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {toTerm} from './toTerm';

export function toQuery(toGraphQL: string): string;
export function toQuery(
variables: {
[key: string]: 'ID' | 'ID!' | 'String!' | 'String' | 'Boolean!' | 'Boolean' | string;
},
toGraphQL: string,
): string;
export function toQuery(...args: Array<any>): string {
return toTerm('query', ...args);
}
12 changes: 12 additions & 0 deletions libs/ngrx-entity-relationship/graphql/src/lib/toSubscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {toTerm} from './toTerm';

export function toSubscription(toGraphQL: string): string;
export function toSubscription(
variables: {
[key: string]: 'ID' | 'ID!' | 'String!' | 'String' | 'Boolean!' | 'Boolean' | string;
},
toGraphQL: string,
): string;
export function toSubscription(...args: Array<any>): string {
return toTerm('subscription', ...args);
}
15 changes: 15 additions & 0 deletions libs/ngrx-entity-relationship/graphql/src/lib/toTerm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function toTerm(term: 'query' | 'subscription' | 'mutation', ...args: Array<any>): string {
const params: Array<string> = [];
let toGraphQL = '';

if (typeof args[0] === 'object') {
for (const key of Object.keys(args[0] || {})) {
params.push(`${key}:${args[0][key]}`);
}
toGraphQL = args[1];
} else {
toGraphQL = args[0];
}

return `${term}${params.length ? `(${params.join(',')})` : ''}${toGraphQL}`;
}
6 changes: 6 additions & 0 deletions libs/ngrx-entity-relationship/graphql/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'ngrx-entity-relationship/augments';

export * from './lib/toGraphQL';
export * from './lib/toMutation';
export * from './lib/toQuery';
export * from './lib/toSubscription';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {toGraphQL} from '../../../../graphql/src/lib/toGraphQL';

describe('toGraphQL', () => {
it('combines strings', () => {
const actual = toGraphQL('1', '2', '3');
expect(actual).toEqual('{1\n2\n3}');
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {of, Subject} from 'rxjs';

import {relationships} from '../../rxjs/src/public_api';
import {HANDLER_ROOT_ENTITIES, HANDLER_ROOT_ENTITY, UNKNOWN} from '../../src/lib/types';
import {relationships} from '../../../../rxjs/src/public_api';
import {HANDLER_ROOT_ENTITIES, HANDLER_ROOT_ENTITY, UNKNOWN} from '../../../../src/lib/types';

describe('operators/relationships', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {UNKNOWN} from '../src/lib/types';
import {childEntity, ENTITY_STATE, FEATURE_SELECTOR, HANDLER_RELATED_ENTITY} from '../src/public_api';
import {UNKNOWN} from '../../../src/lib/types';
import {childEntity, ENTITY_STATE, FEATURE_SELECTOR, HANDLER_RELATED_ENTITY} from '../../../src/public_api';

describe('childEntity', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {childEntitySelector, HANDLER_RELATED_ENTITY} from 'ngrx-entity-relationship';
import {childEntitySelector} from '../../../src/lib/childEntitySelector';
import {HANDLER_RELATED_ENTITY} from '../../../src/lib/types';

describe('childEntitySelector', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {UNKNOWN} from '../src/lib/types';
import {childrenEntities, ENTITY_STATE, FEATURE_SELECTOR, HANDLER_RELATED_ENTITY} from '../src/public_api';
import {UNKNOWN} from '../../../src/lib/types';
import {childrenEntities, ENTITY_STATE, FEATURE_SELECTOR, HANDLER_RELATED_ENTITY} from '../../../src/public_api';

describe('childrenEntities', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {childrenEntitiesSelector, HANDLER_RELATED_ENTITY} from 'ngrx-entity-relationship';
import {childrenEntitiesSelector} from '../../../src/lib/childrenEntitiesSelector';
import {HANDLER_RELATED_ENTITY} from '../../../src/lib/types';

describe('childrenEntitiesSelector', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {UNKNOWN} from '../src/lib/types';
import {ENTITY_STATE, FEATURE_SELECTOR, HANDLER_RELATED_ENTITY, relatedEntity} from '../src/public_api';
import {UNKNOWN} from '../../../src/lib/types';
import {ENTITY_STATE, FEATURE_SELECTOR, HANDLER_RELATED_ENTITY, relatedEntity} from '../../../src/public_api';

describe('relatedEntity', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {HANDLER_RELATED_ENTITY, relatedEntitySelector} from 'ngrx-entity-relationship';
import {relatedEntitySelector} from '../../../src/lib/relatedEntitySelector';
import {HANDLER_RELATED_ENTITY} from '../../../src/lib/types';

describe('relatedEntitySelector', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import {
FEATURE_SELECTOR,
HANDLER_ROOT_ENTITY,
ID_SELECTOR,
ID_TYPES,
rootEntities,
rootEntity,
rootEntityFlags,
} from 'ngrx-entity-relationship';
import {rootEntities} from '../../../src/lib/rootEntities';
import {rootEntity} from '../../../src/lib/rootEntity';
import {rootEntityFlags} from '../../../src/lib/rootEntityFlags';
import {FEATURE_SELECTOR, HANDLER_ROOT_ENTITY, ID_SELECTOR, ID_TYPES} from '../../../src/lib/types';

describe('rootEntities', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {FEATURE_SELECTOR, UNKNOWN} from '../src/lib/types';
import {ENTITY_STATE, HANDLER_RELATED_ENTITY, rootEntity, rootEntityFlags} from '../src/public_api';
import {FEATURE_SELECTOR, UNKNOWN} from '../../../src/lib/types';
import {ENTITY_STATE, HANDLER_RELATED_ENTITY, rootEntity, rootEntityFlags} from '../../../src/public_api';

describe('rootEntity', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {ENTITY_STATE, HANDLER_RELATED_ENTITY, rootEntitySelector} from 'ngrx-entity-relationship';
import {rootEntitySelector} from '../../../src/lib/rootEntitySelector';
import {ENTITY_STATE, HANDLER_RELATED_ENTITY} from '../../../src/lib/types';

describe('rootEntitySelector', () => {
interface Entity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {rootEntity} from '../../../../src/lib/rootEntity';
import {
ngrxEntityRelationshipActions,
ReduceFlat,
reduceFlat,
ReduceGraph,
ReduceFlat,
reduceGraph,
rootEntity,
} from 'ngrx-entity-relationship';
ReduceGraph,
} from '../../../../src/lib/store/actions';

describe('store/actions', () => {
it('ReduceFlat', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {fromFlat} from '../../src/lib/store/fromFlat';
import {injectEntity} from '../../src/lib/store/injectEntity';
import {relatedEntity, rootEntity} from '../../src/public_api';
import {fromFlat} from '../../../../src/lib/store/fromFlat';
import {injectEntity} from '../../../../src/lib/store/injectEntity';
import {relatedEntity, rootEntity} from '../../../../src/public_api';

describe('store/fromFlat', () => {
let injectEntitySpy: jasmine.Spy;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {fromGraph} from '../../src/lib/store/fromGraph';
import {injectEntity} from '../../src/lib/store/injectEntity';
import {relatedEntity, rootEntity} from '../../src/public_api';
import {fromGraph} from '../../../../src/lib/store/fromGraph';
import {injectEntity} from '../../../../src/lib/store/injectEntity';
import {relatedEntity, rootEntity} from '../../../../src/public_api';

describe('store/fromGraph', () => {
let injectEntitySpy: jasmine.Spy;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {injectEntity} from '../../src/lib/store/injectEntity';
import {patchState} from '../../src/lib/store/patchState';
import {rootEntity} from '../../src/public_api';
import {injectEntity} from '../../../../src/lib/store/injectEntity';
import {patchState} from '../../../../src/lib/store/patchState';
import {rootEntity} from '../../../../src/public_api';

describe('store/patchState', () => {
let patchStateSpy: jasmine.Spy;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {fromFlat} from '../../src/lib/store/fromFlat';
import {fromGraph} from '../../src/lib/store/fromGraph';
import {ngrxEntityRelationshipActions, ngrxEntityRelationshipReducer} from '../../src/public_api';
import {fromFlat} from '../../../../src/lib/store/fromFlat';
import {fromGraph} from '../../../../src/lib/store/fromGraph';
import {ngrxEntityRelationshipActions, ngrxEntityRelationshipReducer} from '../../../../src/public_api';

describe('store/ngrxEntityRelationshipReducer', () => {
let fromGraphSpy: jasmine.Spy;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {patchState} from '../../src/lib/store/patchState';
import {patchState} from '../../../../src/lib/store/patchState';

describe('store/patchState', () => {
it('touches only objects', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {argsToArray, mergeCache, normalizeSelector, objectValues, verifyCache} from '../src/lib/utils';
import {argsToArray, mergeCache, normalizeSelector, objectValues, verifyCache} from '../../../src/lib/utils';

describe('utils', () => {
describe('normalizeSelector', () => {
Expand Down

0 comments on commit cea8161

Please sign in to comment.