diff --git a/packages/core/src/edge/common.ts b/packages/core/src/edge/common.ts index d6904f2..e67face 100644 --- a/packages/core/src/edge/common.ts +++ b/packages/core/src/edge/common.ts @@ -1,8 +1,7 @@ import { ObjectOrValue } from '../ts-helpers'; -export type ProjectionScalars = string | boolean | 0 | 1; -export type RawProjection = ObjectOrValue; -export type Projection = { [name: string]: ProjectionScalars | T }; +export type RawProjection = ObjectOrValue; +export type Projection = { [name: string]: string | false | T }; export function capitalize(s: string) { return s.charAt(0).toUpperCase() + s.slice(1); diff --git a/packages/core/src/edge/edge-builder.ts b/packages/core/src/edge/edge-builder.ts index 5edd79b..0458738 100644 --- a/packages/core/src/edge/edge-builder.ts +++ b/packages/core/src/edge/edge-builder.ts @@ -11,6 +11,7 @@ import { Edge } from './edge'; import { capitalize, RawProjection, Projection } from './common'; import { DirectiveBuilder } from '../directive'; import { Ref } from '../ref'; +import { FieldBuilder, BuildFieldArgs } from '../field'; type OpBuilders = OperatorBuilder | LogicalOperatorBuilder; @@ -18,8 +19,7 @@ export interface NameGenerators { param: ParamNameGen; } -export interface BuildEdgeArgs { - field: string; +export interface BuildEdgeArgs extends BuildFieldArgs { nameGen?: NameGenerators; } @@ -29,22 +29,22 @@ export const defaultNameGen = (): NameGenerators => ({ /** should match EdgeBuilder's constructor */ export interface EdgeBuilderConstructor { - (edges: EdgeBuilder | RawProjection): EdgeBuilder; - (type: string, edges: EdgeBuilder | RawProjection): EdgeBuilder; + (edges: EdgeBuilder | RawProjection): EdgeBuilder; + (type: string, edges: EdgeBuilder | RawProjection): EdgeBuilder; } -export class EdgeBuilder { +export class EdgeBuilder extends FieldBuilder { protected type?: string; protected _autoType?: boolean - protected edges: Projection = {}; + protected edges: Projection = {}; protected directives: Record = {}; protected args: ArgsBuilder = new ArgsBuilder(); - protected _varName?: string; constructor( - type?: string | EdgeBuilder | RawProjection, - edges?: EdgeBuilder | RawProjection + type?: string | EdgeBuilder | RawProjection, + edges?: EdgeBuilder | RawProjection ) { + super(''); if (type) { if (typeof type !== 'string') return new EdgeBuilder(undefined, type); @@ -58,7 +58,7 @@ export class EdgeBuilder { } protected setEdges( - edges: EdgeBuilder | RawProjection, + edges: EdgeBuilder | RawProjection, overwrite = false ) { if (edges instanceof EdgeBuilder) @@ -66,24 +66,44 @@ export class EdgeBuilder { this.edges = Object.entries(edges) .reduce((r, [k, v]) => { + const existing = r[k]; + if (v === 1 || v === true) { + if (existing) return r; + v = new FieldBuilder(undefined); + } if (typeof v !== 'object') { + r[k] = v || false; + return r; + } + + // clone value + if ((v instanceof FieldBuilder) && !(v instanceof EdgeBuilder)) + v = new FieldBuilder(undefined).merge(v); + else + v = new EdgeBuilder(this.type ? k : undefined, v); + + if (!existing || typeof existing === 'string') { r[k] = v; return r; } - if (!(r[k] instanceof EdgeBuilder)) - r[k] = new EdgeBuilder(this.type ? k : undefined, v); - else if (!(v instanceof EdgeBuilder)) - (r[k] as EdgeBuilder).setEdges(v); + + if (existing instanceof EdgeBuilder) + r[k] = existing.merge(v, overwrite); else - (r[k] as EdgeBuilder).merge(v, overwrite); + r[k] = v.merge(existing.merge(v)); + return r; }, overwrite ? {} : this.edges); } - protected merge(edge: EdgeBuilder, overwrite = false) { + merge(edge: EdgeBuilder | FieldBuilder, overwrite = false) { + if (edge instanceof FieldBuilder) + super.merge(edge); + if (!(edge instanceof EdgeBuilder)) + return this; + this.setEdges(edge.edges, overwrite); - if (edge._varName) this._varName = edge._varName; if (edge._autoType !== undefined) this._autoType = edge._autoType; if (!edge.autoType) this.type = edge.type; Object.assign(this.directives, edge.directives); @@ -171,17 +191,6 @@ export class EdgeBuilder { return this; } - /** @internal */ - get varName() { - return this._varName; - } - - /** @internal */ - asVar(varName: string) { - this._varName = varName; - return this; - } - keyToField(key: string) { if (['id', 'uid'].includes(key)) return 'uid'; @@ -202,7 +211,7 @@ export class EdgeBuilder { [])); } - protected buildEdgeArgs(field: string, nameGen?: NameGenerators) { + protected buildEdgeArgs(name: string, nameGen?: NameGenerators) { nameGen = Object.assign(defaultNameGen(), nameGen); const args = this.args.build(argMap => { @@ -221,14 +230,15 @@ export class EdgeBuilder { .reduce((r, [k, v]) => { const fieldFromKey = this.keyToField(k); if (v instanceof EdgeBuilder) - r[k] = v.build({ nameGen, field: fieldFromKey }); - else if (v === true || v === 1) r[k] = fieldFromKey; + r[k] = v.build({ nameGen, name: fieldFromKey }); + else if (v instanceof FieldBuilder) + r[k] = v.build({ name: fieldFromKey }); else r[k] = v; return r; }, {}); return { - field, + name, args, edges, varName: this._varName, @@ -245,7 +255,7 @@ export class EdgeBuilder { A extends BuildEdgeArgs >(args: Partial = {}) { return new Edge( - this.buildEdgeArgs(args.field || '', args.nameGen) + this.buildEdgeArgs(args.name || this._name || '', args.nameGen) ); } diff --git a/packages/core/src/edge/edge.ts b/packages/core/src/edge/edge.ts index 5fde5ef..c86cfbb 100644 --- a/packages/core/src/edge/edge.ts +++ b/packages/core/src/edge/edge.ts @@ -2,6 +2,7 @@ import { Args } from '../args'; import { indenter } from './common'; import { Param } from '../param'; import { Directive } from '../directive/directive'; +import { FieldArgs, Field } from '../field'; interface ParamsExtractable { params(): Param[]; @@ -9,26 +10,21 @@ interface ParamsExtractable { type Projection = { [alias: string]: string | Edge }; -export interface EdgeArgs { - field: string; +export interface EdgeArgs extends FieldArgs { edges: Projection; args?: Args; - varName?: string; directives: Record; } -export class Edge { - protected field: string; +export class Edge extends Field { protected edges: Projection; protected _params: Param[]; protected args: Args; - protected varName?: string; protected directives: Record = {}; constructor(args: EdgeArgs) { - this.field = args.field; + super(args); this.edges = args.edges; - this.varName = args.varName; this.directives = args.directives; this.args = args.args || new Args(); this._params = this.params(); @@ -45,9 +41,7 @@ export class Edge { } protected fieldStr() { - if (this.varName) - return `${this.varName} as ${this.field} `; - return this.field + ' '; + return super.toString() + ' '; } protected argsStr() { diff --git a/packages/core/src/field/field-builder.ts b/packages/core/src/field/field-builder.ts new file mode 100644 index 0000000..301b1b0 --- /dev/null +++ b/packages/core/src/field/field-builder.ts @@ -0,0 +1,40 @@ +import { Field } from './field'; + +export interface BuildFieldArgs { + name?: string; +} + +export class FieldBuilder { + protected _name: string; + protected _varName?: string; + + constructor(name: string) { + this._name = name; + } + + /** @internal */ + get varName() { + return this._varName; + } + + /** @internal */ + asVar(varName: string) { + this._varName = varName; + return this; + } + + merge(field: FieldBuilder) { + if (field._name) this._name = field._name; + if (field._varName) this._varName = field._varName; + return this; + } + + build< + A extends BuildFieldArgs + >(args: Partial = {}): Field { + return new Field({ + name: this._name || args.name, + varName: this.varName, + }); + } +} diff --git a/packages/core/src/field/field.ts b/packages/core/src/field/field.ts new file mode 100644 index 0000000..90802c6 --- /dev/null +++ b/packages/core/src/field/field.ts @@ -0,0 +1,20 @@ +export interface FieldArgs { + name: string; + varName?: string; +} + +export class Field { + protected name: string; + protected varName?: string; + + constructor(args: FieldArgs) { + this.name = args.name; + this.varName = args.varName; + } + + toString() { + if (this.varName) + return `${this.varName} as ${this.name}`; + return this.name; + } +} diff --git a/packages/core/src/field/index.ts b/packages/core/src/field/index.ts new file mode 100644 index 0000000..7b8c6d7 --- /dev/null +++ b/packages/core/src/field/index.ts @@ -0,0 +1,2 @@ +export * from './field-builder'; +export * from './field'; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 5951104..2cb0a52 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,13 +1,19 @@ import { EdgeBuilder, EdgeBuilderConstructor } from './edge'; import { QueryBuilder, CombinedQueryBuilder } from './query'; +import { FieldBuilder } from './field'; export * from './uid'; +export * from './field'; export * from './edge'; export * as operator from './operator'; export * as operators from './operators'; export * as param from './param/param'; export * as params from './params'; +export const field = ( + ...args: ConstructorParameters +) => new FieldBuilder(...args); + export const edge: EdgeBuilderConstructor = (...args: any[]) => new EdgeBuilder(...args); diff --git a/packages/core/src/query/query-builder.ts b/packages/core/src/query/query-builder.ts index a166e38..9dffad7 100644 --- a/packages/core/src/query/query-builder.ts +++ b/packages/core/src/query/query-builder.ts @@ -29,16 +29,14 @@ export const defaultNameGen = (): NameGenerators => ({ }); export class QueryBuilder extends EdgeBuilder { - protected queryName?: string; - - constructor(type?: string, queryName?: string) { + constructor(type?: string, name?: string) { super(type, {}); - this.queryName = queryName; + this._name = name; } /** set query name */ name(name: string) { - this.queryName = name; + this._name = name; return this; } @@ -66,10 +64,7 @@ export class QueryBuilder extends EdgeBuilder { buildQueryArgs(nameGen?: NameGenerators) { nameGen = Object.assign(defaultNameGen(), nameGen); - return { - ...super.buildEdgeArgs('', nameGen), - queryName: this.queryName || nameGen.query.next(), - }; + return super.buildEdgeArgs(this._name || nameGen.query.next(), nameGen); } build< diff --git a/packages/core/src/query/query.ts b/packages/core/src/query/query.ts index 6885078..777ae82 100644 --- a/packages/core/src/query/query.ts +++ b/packages/core/src/query/query.ts @@ -2,17 +2,11 @@ import { Edge, EdgeArgs } from '../edge'; import { Param } from '../param'; export interface QueryArgs extends EdgeArgs { - queryName: string; } export class Query extends Edge { constructor(args: QueryArgs) { super(args); - this.field = args.queryName; - } - - get queryName() { - return this.field; } protected fieldStr() { diff --git a/packages/core/test/query.test.ts b/packages/core/test/query.test.ts index 6c618bb..31c8c97 100644 --- a/packages/core/test/query.test.ts +++ b/packages/core/test/query.test.ts @@ -40,13 +40,11 @@ describe('Query test', () => { }, }); - expect(edgesToObject(q)).toMatchObject({ - a: 0, - b: { - c: 0, - d: 1, - }, - }); + const edges = edgesToObject(q); + + expect(edges.a).toEqual(false); + expect(edges.b.c).toEqual(false); + expect(edges.b.d).toBeTruthy(); }); it('query.project should overwrite projections if flag is true', () => { @@ -64,7 +62,7 @@ describe('Query test', () => { const edges = edgesToObject(q); - expect(edges.a).toEqual(0); + expect(edges.a).toEqual(false); expect(edges.b).toEqual(undefined) }); });