Skip to content

Commit

Permalink
Merge pull request #18 from specialblend/refactor/optimize
Browse files Browse the repository at this point in the history
refactor: clean up and optimize
  • Loading branch information
specialblend authored Nov 20, 2021
2 parents abc2f2a + e2b3b97 commit bdf5246
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 112 deletions.
1 change: 0 additions & 1 deletion example/GenerateArgoWorkflow.example.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ test("GenerateArgoWorkflow", () => {
defaultValue: "hello world!",
},
],
templates: [{}],
};
const query = gql`
query GenerateArgoWorkflow {
Expand Down
37 changes: 26 additions & 11 deletions src/contract.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,56 @@
import { ExecInfo } from "graphql-anywhere";
import type { ExecInfo } from "graphql-anywhere";
import type { DirectiveInfo } from "apollo-utilities/src/directives";

import { DIRECTIVES } from "./transform";

export type Maybe<T> = T | undefined;
export type PathSelector = string | "@";

export type JsonParent = JsonRecord | JsonList;
export type JsonChild = string | number | boolean | null | JsonParent;
export type JsonRecord = { [k: string]: JsonChild };
export type JsonList = JsonChild[];

export type JsonSelector = JsonChild | undefined;

export type PathSelector = string | "@";

export interface Filter {
from?: PathSelector;
match?: JsonSelector;
noMatch?: JsonSelector;
}

export type ExecRoot = any;
export type ExecCtx = any;
export type ExecSource = JsonRecord;

export interface ExecArgs {
from?: PathSelector;
filter?: Filter;
}
export type ExecCtx = any;
export type ExecSource = JsonRecord;
export type ExecParent = any;
export type ExecChild = JsonChild;

export type Exec = {
fieldName: string;
root: ExecRoot;
args: ExecArgs;
context: ExecCtx;
info: ExecInfo;
data?: ExecSource;
parent?: ExecParent;
child?: ExecChild;
};

export type ExecInfoConst = ExecInfo & {
directives: DirectiveInfo & {
const: {
of: string;
};
};
};

export type HasConst = Exec & {
info: ExecInfoConst;
};

export type HasFilter = Exec & {
args: ExecArgs & {
filter: Filter;
};
};

export type DirectiveMap = typeof DIRECTIVES;
Expand Down
135 changes: 71 additions & 64 deletions src/exec.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,49 @@
import { Exec, ExecSource, JsonChild, Maybe } from "./contract";
import type {
Exec,
ExecSource,
HasConst,
HasFilter,
JsonChild,
Maybe,
} from "./contract";

import { path } from "./path";
import { filter } from "./filter";
import { mapDxIds, pipeDx } from "./transform";
import { fixInfo, isset, orEmpty } from "./util";

function isConst(ex: Exec): any {
function isConst(ex: Exec): ex is HasConst {
const {
info: {
isLeaf,
directives: { const: constTag },
},
info: { isLeaf, directives: { const: _const = {} } = {} },
} = ex;
if (isLeaf && constTag) {
const { of: constValue } = constTag;
if (isset(constValue)) {
return constValue;
}
}
return isLeaf && isset(_const.of);
}

function shouldExPath(ex: Exec) {
const {
args: { from: pathSelector, filter: filterSelector },
args: { from: pathSelector, filter },
info: {
isLeaf,
directives: { map: mapTag, nomap: noMapTag },
directives: { map, nomap },
field: { directives: directiveNodes = [] },
},
} = ex;

const directiveIds = mapDxIds(directiveNodes);
if (isset(nomap)) {
return false;
}

return (
!isset(noMapTag) &&
(isLeaf ||
pathSelector ||
isset(mapTag) ||
isset(filterSelector) ||
directiveIds.length)
isLeaf ||
pathSelector ||
isset(map) ||
isset(filter) ||
mapDxIds(directiveNodes).length
);
}

function applyEx(fn: (ex: Exec) => any) {
return function (
return function apply(
fieldName: string,
root: any,
args: any,
Expand All @@ -59,77 +60,83 @@ function applyEx(fn: (ex: Exec) => any) {
};
}

function execPath(source: ExecSource) {
return function (ex: Exec): Maybe<JsonChild> {
const { fieldName, root, args, info } = ex;
function hasFilter(ex: Exec): ex is HasFilter {
return isset(ex.args.filter);
}

function execPath(ex: Exec) {
return function select(data: ExecSource) {
const {
fieldName,
root,
args: { from },
} = ex;
if (shouldExPath(ex)) {
const { from: selector } = args;
const pathName = isset(selector) ? selector : fieldName;
return path(pathName, source, root);
const pathName = isset(from) ? from : fieldName;
return path(pathName, data, root);
}
return root;
};
}

function execFilter(source: ExecSource, child: Maybe<JsonChild>) {
return function (ex: Exec): Maybe<JsonChild> {
const { root, args } = ex;
const { filter: query } = args;
if (isset(query)) {
const { from, match, noMatch } = query;
function execFilter(ex: Exec) {
return function (data: ExecSource, child: Maybe<JsonChild>) {
if (hasFilter(ex)) {
const {
root,
args: {
filter: { from, match, noMatch },
},
} = ex;
const filterData = filter(match, noMatch);
if (isset(from)) {
const target = path(from, source, root);
const target = path(from, data, root);
if (isset(target)) {
return filter(match, noMatch, target, child);
return filterData(target, child);
}
return;
}
if (isset(child)) {
return filter(match, noMatch, child);
return filterData(child);
}
}
return child;
};
}
function execTransform(child: Maybe<JsonChild>) {
return function (ex: Exec): any {

function execTransform(ex: Exec) {
return function transform(child: Maybe<JsonChild>) {
const {
info: {
directives: { default: defaultTag },
directives: { default: { to: fallback } = { to: undefined } },
},
} = ex;
const exec = pipeDx(ex);
if (isset(child)) {
return exec(child);
}
if (isset(defaultTag)) {
const { to: defaultValue } = defaultTag;
if (isset(defaultValue)) {
return defaultValue;
}
return pipeDx(ex)(child);
}
return fallback;
};
}

function execConst(source: ExecSource) {
return function execConst(ex: Exec) {
const constValue = isConst(ex);
if (isset(constValue)) {
return constValue;
}
};
function execConst(ex: HasConst) {
const {
info: {
directives: {
const: { of: value },
},
},
} = ex;
return value;
}

export function exec(source: ExecSource) {
export function exec(data: ExecSource) {
return applyEx(function exec(ex: Exec): any {
const constValue = execConst(source)(ex);
if (isset(constValue)) {
return constValue;
if (isConst(ex)) {
return execConst(ex);
}
const selected = execPath(source)(ex);
const filtered = execFilter(source, selected)(ex);
const transformed = execTransform(filtered)(ex);
const [result] = [transformed];
return result;
const select = execPath(ex);
const filter = execFilter(ex);
const transform = execTransform(ex);
return transform(filter(data, select(data)));
});
}
6 changes: 3 additions & 3 deletions src/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ describe("filter", () => {
...match,
charlie: "#charlie",
};
const result = filter(match, undefined, root);
const result = filter(match, undefined)(root);
expect(result).toBe(root);
});
test("it returns nothing when match does not match root", () => {
Expand All @@ -120,7 +120,7 @@ describe("filter", () => {
bravo: "#notbravo",
charlie: "#charlie",
};
const result = filter(match, undefined, root);
const result = filter(match, undefined)(root);
expect(result).toBeUndefined();
});
test("it returns array values matching match", () => {
Expand Down Expand Up @@ -160,7 +160,7 @@ describe("filter", () => {
},
];
const root = [...matchesSelector, ...notMatchesSelector];
const result = filter(match, undefined, root);
const result = filter(match, undefined)(root);
expect(result).toEqual(matchesSelector);
});
});
35 changes: 14 additions & 21 deletions src/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,33 @@ import { islist, isrecord, isset } from "./util";
export function matches(
selector: JsonSelector,
data: JsonChild,
defaultTo = true
_default = true
): boolean {
if (isset(selector)) {
if (isrecord(selector)) {
if (isrecord(data)) {
const childKeys = keys(selector);
return all((k: string | number) => {
const childData = selector[k];
const parentData = data[k];
return matches(childData, parentData);
})(childKeys);
return all((k) => matches(selector[k], data[k]), keys(selector));
}
return false;
}
return equals(selector, data);
}
return defaultTo;
return _default;
}

export function filter(
match: JsonSelector = undefined,
noMatch: JsonSelector = undefined,
parent: JsonChild,
child = parent
): any {
if (!isset(match) && !isset(noMatch)) {
return child;
}
if (islist(parent)) {
return parent.filter((child) => filter(match, noMatch, child));
}
if (matches(match, parent)) {
if (!matches(noMatch, parent, false)) {
noMatch: JsonSelector = undefined
) {
return function filter(parent: JsonChild, child = parent): any {
if (!isset(match) && !isset(noMatch)) {
return child;
}
}
if (islist(parent)) {
return parent.filter((child) => filter(child));
}
if (matches(match, parent) && !matches(noMatch, parent, false)) {
return child;
}
};
}
4 changes: 2 additions & 2 deletions src/map.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DocumentNode } from "graphql";
import { JsonRecord } from "./contract";
import type { DocumentNode } from "graphql";
import type { JsonRecord } from "./contract";
import graphql from "graphql-anywhere";
import { exec } from "./exec";

Expand Down
13 changes: 5 additions & 8 deletions src/path.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import {
import type {
JsonChild,
JsonParent,
JsonRecord,
Maybe,
PathSelector,
} from "./contract";

import jp from "jsonpath";
import { isset } from "./util";

function jsonpath(selector: string, data: JsonParent): Maybe<JsonChild> {
try {
const [child] = jp.query(data, selector);
if (isset(child)) {
return child;
}
} catch (err) {
throw err;
const [child] = jp.query(data, selector);
if (isset(child)) {
return child;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JsonList, JsonRecord } from "./contract";
import { ExecInfo } from "graphql-anywhere";
import type { JsonList, JsonRecord } from "./contract";
import type { ExecInfo } from "graphql-anywhere";

export function isset<T>(x: T | undefined): x is T {
return typeof x !== "undefined";
Expand Down

0 comments on commit bdf5246

Please sign in to comment.