Skip to content

Commit

Permalink
QueryBuilder array handling (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
willmarks committed May 2, 2023
1 parent 51a3d9a commit 321b531
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 44 deletions.
3 changes: 1 addition & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import {makeAxiosInstanceWithRetry} from './axios';
import {wrapApiError} from './errors';
import {paginatedQuery} from './graphql/graphql';
import {batchMutation} from './graphql/query-builder';
import {Schema} from './graphql/types';
import {Mutation, Schema} from './graphql/types';
import {
Account,
FarosClientConfig,
GraphVersion,
Location,
Model,
Mutation,
NamedQuery,
Phantom,
SecretName,
Expand Down
30 changes: 21 additions & 9 deletions src/graphql/query-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
Mutation,
MutationObject,
MutationReference,
} from '../types';
} from './types';

export interface MutationFields {
interface MutationFields {
[field: string]: MutationFieldValue;
}

Expand All @@ -25,12 +25,7 @@ export interface MutationParams extends RefParams {
mask?: string[];
}

interface CategoryDetail {
category: string;
detail: string;
}

export type MutationFieldValue = string | number | CategoryDetail | Ref;
type MutationFieldValue = string | number | boolean | object | Ref;

export class QueryBuilder {
constructor(private readonly origin: string) {}
Expand Down Expand Up @@ -83,7 +78,7 @@ export class QueryBuilder {
// ref's key should be suffixed with Id for onConflict field
fullMask.push(`${k}Id`);
} else {
mutObj[k] = v;
mutObj[k] = Array.isArray(v) ? this.arrayLiteral(v) : v;
fullMask.push(k);
}
}
Expand Down Expand Up @@ -131,6 +126,23 @@ export class QueryBuilder {
update_columns: mask.map((c) => new EnumType(c)),
};
}

/**
* Change string arrays into string literal array.
* Leave non-string arrays untouched.
*/
private arrayLiteral(arr: any[]): string | object {
for (const item of arr) {
if (typeof item !== 'string') {
return arr;
}
}

return JSON.stringify(arr)
.replace('[', '{')
.replace(']', '}')
.replace(/"/g, '');
}
}

export function mask(object: any): string[] {
Expand Down
25 changes: 25 additions & 0 deletions src/graphql/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {EnumType} from 'json-to-graphql-query';
import {Dictionary} from 'ts-essentials';

export interface Table {
Expand Down Expand Up @@ -63,3 +64,27 @@ export interface PathToModel {
readonly path: ReadonlyArray<string>;
readonly modelName: string;
}

export interface Mutation {
mutation: {
[key: string]: {
__args: MutationObject;
id: boolean;
};
};
}

export interface MutationObject {
object: any;
on_conflict: ConflictClause;
}

export interface MutationReference {
data: any;
on_conflict: ConflictClause;
}

export interface ConflictClause {
constraint: EnumType;
update_columns: EnumType[];
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export {
Schema,
PathToModel,
Query,
Mutation
} from './graphql/types';
export {HasuraSchemaLoader} from './graphql/hasura-schema-loader';
export {QueryBuilder, mask, batchMutation} from './graphql/query-builder';
Expand Down
27 changes: 0 additions & 27 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import {EnumType} from 'json-to-graphql-query';

export interface FarosClientConfig {
readonly url: string;
readonly apiKey: string;
Expand Down Expand Up @@ -82,28 +80,3 @@ export interface Model {
keySchema: any;
dataSchema: any;
}

export interface Mutation {
mutation: {
[key: string]: {
__args: MutationObject;
id: boolean;
};
};
}

export interface MutationObject {
object?: any;
data?: any;
on_conflict: ConflictClause;
}

export interface MutationReference {
data: any;
on_conflict: ConflictClause;
}

export interface ConflictClause {
constraint: EnumType;
update_columns: EnumType[];
}
2 changes: 2 additions & 0 deletions test/__snapshots__/query-builder.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`query builder creates mutations 1`] = `"mutation { m0: insert_compute_Application_one (object: {name: \\"<application_name>\\", platform: \\"<application_platform>\\", origin: \\"test-origin\\"}, on_conflict: {constraint: compute_Application_pkey, update_columns: [name, platform, origin]}) { id } m1: insert_cicd_Organization_one (object: {uid: \\"<organization_uid>\\", source: \\"<organization_source>\\", origin: \\"test-origin\\"}, on_conflict: {constraint: cicd_Organization_pkey, update_columns: [uid, source, origin]}) { id } m2: insert_cicd_Pipeline_one (object: {uid: \\"<pipeline_uid>\\", organization: {data: {uid: \\"<organization_uid>\\", source: \\"<organization_source>\\"}, on_conflict: {constraint: cicd_Organization_pkey, update_columns: [refreshedAt]}}, origin: \\"test-origin\\"}, on_conflict: {constraint: cicd_Pipeline_pkey, update_columns: [uid, organizationId, origin]}) { id } m3: insert_cicd_Build_one (object: {uid: \\"<cicd_Build>\\", pipeline: {data: {uid: \\"<pipeline_uid>\\", organization: {data: {uid: \\"<organization_uid>\\", source: \\"<organization_source>\\"}, on_conflict: {constraint: cicd_Organization_pkey, update_columns: [refreshedAt]}}}, on_conflict: {constraint: cicd_Pipeline_pkey, update_columns: [refreshedAt]}}, name: \\"<build_name>\\", origin: \\"test-origin\\"}, on_conflict: {constraint: cicd_Build_pkey, update_columns: [uid, pipelineId, name, origin]}) { id } m4: insert_cicd_Deployment_one (object: {uid: \\"<deployment_uid\\", source: \\"<deployment_source>\\", application: {data: {name: \\"<application_name>\\", platform: \\"<application_platform>\\"}, on_conflict: {constraint: compute_Application_pkey, update_columns: [refreshedAt]}}, build: {data: {uid: \\"<cicd_Build>\\", pipeline: {data: {uid: \\"<pipeline_uid>\\", organization: {data: {uid: \\"<organization_uid>\\", source: \\"<organization_source>\\"}, on_conflict: {constraint: cicd_Organization_pkey, update_columns: [refreshedAt]}}}, on_conflict: {constraint: cicd_Pipeline_pkey, update_columns: [refreshedAt]}}}, on_conflict: {constraint: cicd_Build_pkey, update_columns: [refreshedAt]}}, status: {category: \\"Success\\", detail: \\"<status_detail>\\"}, origin: \\"test-origin\\"}, on_conflict: {constraint: cicd_Deployment_pkey, update_columns: [uid, source, applicationId, buildId, status, origin]}) { id } }"`;

exports[`query builder creates mutations with non-model objects 1`] = `"mutation { m0: insert_qa_TestCase_one (object: {uid: \\"<uid>\\", source: \\"<source>\\", name: \\"<name>\\", before: [{description: \\"<description>\\", condition: \\"<condition>\\"}], after: [{description: \\"<description>\\", condition: \\"<condition>\\"}], tags: \\"{tag1,tag2}\\", origin: \\"test-origin\\"}, on_conflict: {constraint: qa_TestCase_pkey, update_columns: [uid, source, name, before, after, tags, origin]}) { id } }"`;
30 changes: 24 additions & 6 deletions test/query-builder.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import * as sut from '../src/graphql/query-builder';
import {Mutation} from '../src/types';

const ORIGIN = 'test-origin';

describe('query builder', () => {
test('creates mutations', () => {
const qb = new sut.QueryBuilder(ORIGIN);
const mutations: Mutation[] = [];

const application = {
model: 'compute_Application',
Expand Down Expand Up @@ -55,16 +53,36 @@ describe('query builder', () => {
},
};

mutations.push(
const mutations = [
qb.upsert(application),
qb.upsert(organization),
qb.upsert(pipeline),
qb.upsert(build),
qb.upsert(deployment)
);

qb.upsert(deployment),
];
const queryString = sut.batchMutation(mutations);
expect(queryString).toMatchSnapshot();
});

test('creates mutations with non-model objects', () => {
const qb = new sut.QueryBuilder(ORIGIN);

const testCase: sut.MutationParams = {
model: 'qa_TestCase',
key: {
uid: '<uid>',
source: '<source>',
},
body: {
name: '<name>',
before: [{description: '<description>', condition: '<condition>'}],
after: [{description: '<description>', condition: '<condition>'}],
tags: ['tag1', 'tag2'],
},
};

const mutations = [qb.upsert(testCase)];
const queryString = sut.batchMutation(mutations);
expect(queryString).toMatchSnapshot();
});
});

0 comments on commit 321b531

Please sign in to comment.