Skip to content

Commit

Permalink
feat(core): implement Field primitive
Browse files Browse the repository at this point in the history
  • Loading branch information
binier committed Aug 6, 2020
1 parent d9a2022 commit a87362e
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 70 deletions.
5 changes: 2 additions & 3 deletions packages/core/src/edge/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ObjectOrValue } from '../ts-helpers';

export type ProjectionScalars = string | boolean | 0 | 1;
export type RawProjection<T> = ObjectOrValue<ProjectionScalars | T>;
export type Projection<T> = { [name: string]: ProjectionScalars | T };
export type RawProjection<T> = ObjectOrValue<string | boolean | 0 | 1 | T>;
export type Projection<T> = { [name: string]: string | false | T };

export function capitalize(s: string) {
return s.charAt(0).toUpperCase() + s.slice(1);
Expand Down
76 changes: 43 additions & 33 deletions packages/core/src/edge/edge-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ 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;

export interface NameGenerators {
param: ParamNameGen;
}

export interface BuildEdgeArgs {
field: string;
export interface BuildEdgeArgs extends BuildFieldArgs {
nameGen?: NameGenerators;
}

Expand All @@ -29,22 +29,22 @@ export const defaultNameGen = (): NameGenerators => ({

/** should match EdgeBuilder's constructor */
export interface EdgeBuilderConstructor {
(edges: EdgeBuilder | RawProjection<EdgeBuilder>): EdgeBuilder;
(type: string, edges: EdgeBuilder | RawProjection<EdgeBuilder>): EdgeBuilder;
(edges: EdgeBuilder | RawProjection<EdgeBuilder | FieldBuilder>): EdgeBuilder;
(type: string, edges: EdgeBuilder | RawProjection<EdgeBuilder | FieldBuilder>): EdgeBuilder;
}

export class EdgeBuilder {
export class EdgeBuilder extends FieldBuilder {
protected type?: string;
protected _autoType?: boolean
protected edges: Projection<EdgeBuilder> = {};
protected edges: Projection<EdgeBuilder | FieldBuilder> = {};
protected directives: Record<string, DirectiveBuilder> = {};
protected args: ArgsBuilder = new ArgsBuilder();
protected _varName?: string;

constructor(
type?: string | EdgeBuilder | RawProjection<EdgeBuilder>,
edges?: EdgeBuilder | RawProjection<EdgeBuilder>
type?: string | EdgeBuilder | RawProjection<EdgeBuilder | FieldBuilder>,
edges?: EdgeBuilder | RawProjection<EdgeBuilder | FieldBuilder>
) {
super('');
if (type) {
if (typeof type !== 'string')
return new EdgeBuilder(undefined, type);
Expand All @@ -58,32 +58,52 @@ export class EdgeBuilder {
}

protected setEdges(
edges: EdgeBuilder | RawProjection<EdgeBuilder>,
edges: EdgeBuilder | RawProjection<EdgeBuilder | FieldBuilder>,
overwrite = false
) {
if (edges instanceof EdgeBuilder)
return this.setEdges(edges.edges, overwrite);

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);
Expand Down Expand Up @@ -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';
Expand All @@ -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 => {
Expand All @@ -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,
Expand All @@ -245,7 +255,7 @@ export class EdgeBuilder {
A extends BuildEdgeArgs
>(args: Partial<A> = {}) {
return new Edge(
this.buildEdgeArgs(args.field || '', args.nameGen)
this.buildEdgeArgs(args.name || this._name || '', args.nameGen)
);
}

Expand Down
16 changes: 5 additions & 11 deletions packages/core/src/edge/edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,29 @@ 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[];
}

type Projection = { [alias: string]: string | Edge };

export interface EdgeArgs {
field: string;
export interface EdgeArgs extends FieldArgs {
edges: Projection;
args?: Args;
varName?: string;
directives: Record<string, Directive>;
}

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<string, Directive> = {};

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();
Expand All @@ -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() {
Expand Down
40 changes: 40 additions & 0 deletions packages/core/src/field/field-builder.ts
Original file line number Diff line number Diff line change
@@ -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<A> = {}): Field {
return new Field({
name: this._name || args.name,
varName: this.varName,
});
}
}
20 changes: 20 additions & 0 deletions packages/core/src/field/field.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
2 changes: 2 additions & 0 deletions packages/core/src/field/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './field-builder';
export * from './field';
6 changes: 6 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -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<typeof FieldBuilder>
) => new FieldBuilder(...args);

export const edge: EdgeBuilderConstructor =
(...args: any[]) => new EdgeBuilder(...args);

Expand Down
13 changes: 4 additions & 9 deletions packages/core/src/query/query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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<
Expand Down
6 changes: 0 additions & 6 deletions packages/core/src/query/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
14 changes: 6 additions & 8 deletions packages/core/test/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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)
});
});

0 comments on commit a87362e

Please sign in to comment.