Skip to content

Commit

Permalink
feat: changes mostly via re-organization of some schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Forrester committed Oct 5, 2019
1 parent aa50485 commit 73af901
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 82 deletions.
Binary file modified README-rest-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 7 additions & 7 deletions README-rest-diagram.seqdiag
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ seqdiag {
s [label="server"];

c -> s [label="GET: /definition/order"];
c <- s [label="200 OK",note="Response (Definition):\n \n1 | {\n2 | \"title\": \"Create order\",\n3 | \"statement\": \"INSERT INTO order (customer_id) VALUES ($customer_id)\n3| RETURNING order_id, customer_id\"\n4 | \"variables\": [{\n5 | \"name\": \"customer_id\",\n6 | \"variable_type\": \"select\",\n7 | \"statement\": \"list_customer\",\n8 | \"display_field\": \"disp\",\n9 | \"value_field\": \"id\"\n10| }]\n11| }\n"]
c <- s [label="200 OK",note="Response (Definition):\n \n1 | {\n2 | \"title\": \"Create order\",\n3 | \"statement\": \"INSERT INTO order (customer_id) VALUES ($customer_id)\n4 | RETURNING order_id, customer_id\"\n5 | \"parameters\": [{\n6 | \"name\": \"customer_id\",\n7 | \"type\": \"select\",\n8 | \"statement\": \"list_customer\",\n9 | \"display_field\": \"disp\",\n10| \"value_field\": \"id\"\n11| }]\n12| }\n"]

c -> s [label="GET: /demand/list_customer"];
c <- s [label="200 OK", note="Response (Result):\n\n1❘ {\n2❘ \"fields\": [\n3| { \"field_name\": \"customer_id\" , \"field_type\": \"integer\" },\n4| { \"field_name\": \"name\" , \"field_type\": \"varchar\" }\n5| ],\n6| \"rows\": [[ 53, \"Fred\" ], [ 54, \"John\"], [ 57, \"Sam\"]]\n7| }"]
c <- s [label="200 OK", note="Response (Result):\n\n1❘ {\n2❘ \"fields\": [\n3| { \"name\": \"customer_id\" , \"type\": \"integer\" },\n4| { \"name\": \"name\" , \"type\": \"varchar\" }\n5| ],\n6| \"rows\": [[ 53, \"Fred\" ], [ 54, \"John\"], [ 57, \"Sam\"]]\n7| }"]

c -> s [label="POST: /request/order", note="Request (RequestCreationParameter):\n\n1| [ {\n2| \"field_name\": \"customer_id\",\n3| \"field_value\": 53 \n4| } ]"];
c <- s [label="202 ACCEPTED\nLocation: /request/order/82", note="Response (RequestCreation):\n\n1| {\n2| \"location\": \"/request/order/82\"\n4| }"]
c -> s [label="POST: /request/order", note="Request (RequestCreation):\n\n1| { \n2| \"arguments\": [ {\n3| \"name\": \"customer_id\",\n4| \"value\": 53 \n5| } ] \n6| }"];
c <- s [label="301 Moved Permanently\nLocation: /request/order/82", note="Response:\n\n1| { \"location\": \"/request/order/82\" }"]

c -> s [label="GET: /request/order/82"]
c <- s [label="202 ACCEPTED", note="Response (Request):\n\n1| { \"status\": \"pending\" }"]
c <- s [label="202 ACCEPTED", note="Request (Request):\n\n1| { \n2| \"status\": \"pending\",\n3| \"arguments\": [ {\n4| \"name\": \"customer_id\",\n5| \"value\": 53 \n6| } ] \n7| }"];

c -> s [label="GET: /request/order/82"]
c <- s [label="301 OK\nLocation: /result/order/74", note="Response (Request):\n\n1| {\n2| \"status\": \"complete\",\n3| \"location\": \"/result/order/74\"\n4| }"]
c <- s [label="301 Moved Permanently\nLocation: /result/order/74", note="Request (Request):\n\n1| { \n2| \"status\": \"complete\",\n3| \"location\": \"/result/order/74\"\n4| \"arguments\": [ {\n5| \"name\": \"customer_id\",\n6| \"value\": 53 \n7| } ] \n8| }"];

c -> s [label="GET /result/order/74"]
c <- s [label="200 OK", note="Response (Result):\n\n1❘ {\n2❘ \"fields\": [\n3| { \"field_name\": \"order_id\" , \"field_type\": \"integer\" },\n4| { \"field_name\": \"customer_id\" , \"field_type\": \"integer\" }\n5| ],\n6| \"rows\": [[ 74, 53 ]]\n7| }"]
c <- s [label="200 OK", note="Response (Result):\n\n1❘ {\n2❘ \"fields\": [\n3| { \"name\": \"order_id\" , \"type\": \"integer\" },\n4| { \"name\": \"customer_id\" , \"type\": \"integer\" }\n5| ],\n6| \"rows\": [[ 74, 53 ]]\n7| }"]

}

22 changes: 11 additions & 11 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,17 @@ curl http:/localhost:8803/result/customer_search/Uz9rkntC9reP
```json
{
"fields": [
{ "field_name": "customer_id", "field_type": "bpchar" },
{ "field_name": "company_name", "field_type": "varchar" },
{ "field_name": "contact_name", "field_type": "varchar" },
{ "field_name": "contact_title", "field_type": "varchar" },
{ "field_name": "address", "field_type": "varchar" },
{ "field_name": "city", "field_type": "varchar" },
{ "field_name": "region", "field_type": "varchar" },
{ "field_name": "postal_code", "field_type": "varchar" },
{ "field_name": "country", "field_type": "varchar" },
{ "field_name": "phone", "field_type": "varchar" },
{ "field_name": "fax", "field_type": "varchar" }
{ "name": "customer_id", "type": "bpchar" },
{ "name": "company_name", "type": "varchar" },
{ "name": "contact_name", "type": "varchar" },
{ "name": "contact_title", "type": "varchar" },
{ "name": "address", "type": "varchar" },
{ "name": "city", "type": "varchar" },
{ "name": "region", "type": "varchar" },
{ "name": "postal_code", "type": "varchar" },
{ "name": "country", "type": "varchar" },
{ "name": "phone", "type": "varchar" },
{ "name": "fax", "type": "varchar" }
],
"rows": [
[
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"typescript": "^3.5.3"
},
"scripts": {
"test": "find test -type f | parallel ts-node {} && npm run-script build && npm run-script lint",
"test": "find test -type f | parallel -j 1 ts-node {} && npm run-script build && npm run-script lint",
"build": "tsc",
"build-doc": "seqdiag ./README-rest-diagram.seqdiag -f /usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf -o README-rest-diagram.png && asciidoctor -r asciidoctor-diagram README.adoc",
"lint": "tslint --project tsconfig.json",
Expand Down
38 changes: 21 additions & 17 deletions src/QueryRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as pg from "pg";
import { FieldDef, QueryResult } from "pg";
import randCryptoString from "random-crypto-string";

import { EsqlateDefinition, EsqlateErrorResult, EsqlateRequestCreationParameter, EsqlateRequestCreationParameterItem, EsqlateResult, EsqlateStatementNormalized, EsqlateVariable, normalize } from "esqlate-lib";
import { EsqlateArgument, EsqlateDefinition, EsqlateErrorResult, EsqlateParameter, EsqlateResult, EsqlateStatementNormalized, normalize } from "esqlate-lib";
import { EsqlateQueueWorker } from "esqlate-queue";

import {ResultId} from "./persistence";
Expand All @@ -27,15 +27,15 @@ export interface ResultCreated {
export interface QueueItem {
definition: EsqlateDefinition;
requestId: string;
serverParameters: EsqlateRequestCreationParameter;
userParameters: EsqlateRequestCreationParameter;
serverParameters: EsqlateArgument[];
userParameters: EsqlateArgument[];
}


export type DemandRunner = (
definition: EsqlateDefinition,
serverParameters: EsqlateRequestCreationParameter,
parameters: EsqlateRequestCreationParameter
serverParameters: EsqlateArgument[],
userParameters: EsqlateArgument[],
) => Promise<EsqlateResult>;


Expand All @@ -45,7 +45,7 @@ export function pgQuery(statement: EsqlateStatementNormalized, inputValues: {[k:
knownValues: string[];
}

function reducer(acc: PgQueryExtra, ed: string | EsqlateVariable): PgQueryExtra {
function reducer(acc: PgQueryExtra, ed: string | EsqlateParameter): PgQueryExtra {

if (typeof ed === "string") {
return {
Expand Down Expand Up @@ -80,17 +80,17 @@ export function pgQuery(statement: EsqlateStatementNormalized, inputValues: {[k:
}


export function getQuery(normalizedStatement: EsqlateStatementNormalized, serverParameters: EsqlateRequestCreationParameter, parameters: EsqlateRequestCreationParameter) {
export function getQuery(normalizedStatement: EsqlateStatementNormalized, serverParameters: EsqlateArgument[], parameters: EsqlateArgument[]) {

function esqlateRequestCreationParameterToOb(acc: { [k: string]: any }, parameter: EsqlateRequestCreationParameterItem): { [k: string]: any } {
function esqlateRequestCreationParameterToOb(acc: { [k: string]: any }, parameter: EsqlateArgument): { [k: string]: any } {
const merger: { [k: string]: any } = {};
merger[parameter.field_name] = parameter.field_value;
merger[parameter.name] = parameter.value;
return { ...acc, ...merger };
}

const inputValues: { [k: string]: any } = serverParameters.reduce(
esqlateRequestCreationParameterToOb,
parameters.reduce(
(parameters || []).reduce(
esqlateRequestCreationParameterToOb,
{},
),
Expand All @@ -104,12 +104,12 @@ export function format(dataTypeIDToName: (dataTypeID: Oid) => string, promiseRes
function success(results: QueryResult): EsqlateResult {
const fields = results.fields.map((f) => {
return {
field_name: f.name,
field_type: dataTypeIDToName(f.dataTypeID),
name: f.name,
type: dataTypeIDToName(f.dataTypeID),
};
});
const rows = results.rows.map((row) => {
return fields.map((fn) => row[fn.field_name]);
return fields.map((fn) => row[fn.name]);
});
return {
fields,
Expand Down Expand Up @@ -174,14 +174,18 @@ export function getLookupOid(pool: pg.Pool) {

export function getDemandRunner(pool: pg.Pool, lookupOid: (oid: number) => string): DemandRunner {

return async function demandRunner(definition: EsqlateDefinition, serverParameters: EsqlateRequestCreationParameter, parameters: EsqlateRequestCreationParameter) {
return async function demandRunner(
definition: EsqlateDefinition,
serverArguments: EsqlateArgument[],
userArguments: EsqlateArgument[],
) {

const normalized = normalize(
definition.variables,
definition.parameters,
definition.statement,
);

const qry = getQuery(normalized, serverParameters, parameters);
const qry = getQuery(normalized, serverArguments, userArguments);

return await format(lookupOid, pool.query(qry));

Expand All @@ -199,7 +203,7 @@ export function getEsqlateQueueWorker(pool: pg.Pool, lookupOid: (oid: number) =>
const result = await demandRunner(
qi.definition,
qi.serverParameters,
qi.userParameters
qi.userParameters,
);

const rand = await randCryptoString(4);
Expand Down
61 changes: 33 additions & 28 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import fs from "fs";
import { join as pathJoin } from "path";

import Ajv from "ajv";
import { EsqlateDefinition, EsqlateRequestCreationParameter, EsqlateRequestCreationParameterItem, EsqlateResult } from "esqlate-lib";
import { EsqlateArgument, EsqlateDefinition, EsqlateRequestCreation, EsqlateResult } from "esqlate-lib";
import { EsqlateQueue } from "esqlate-queue";
import randCryptoString from "random-crypto-string";

import { EsqlateError, EsqlateErrorEnum, EsqlateErrorInvalidDefinition, EsqlateErrorInvalidRequestBody, EsqlateErrorInvalidRequestParameter, EsqlateErrorMissingDefinition, EsqlateErrorMissingLocal, EsqlateErrorMissingVariables, Level, Logger } from "./logger";
import { Persistence } from "./persistence";
import { QueueItem, ResultCreated, DemandRunner } from "./QueryRunner";
import { DemandRunner, QueueItem, ResultCreated } from "./QueryRunner";

import * as schemaRequestCreation from "esqlate-lib/res/schema-request-creation-parameter.json";
import * as schemaRequestCreation from "esqlate-lib/res/schema-request-creation.json";

// TODO: Move types to seperate files

Expand All @@ -24,15 +24,15 @@ export interface ServiceInformation {

export interface ServerVariableRequester {
listServerVariable: (req: Request) => string[];
getServerVariable: (req: Request, name: string) => EsqlateRequestCreationParameterItem["field_value"];
getServerVariable: (req: Request, name: string) => EsqlateArgument["value"];
}

interface NeedsPersistence { persistence: Persistence; }
interface NeedsServiceInformation { serviceInformation: ServiceInformation; }
interface NeedsQueue { queue: EsqlateQueue<QueueItem, ResultCreated>; }
interface NeedsServerVariableRequester { serverVariableRequester: ServerVariableRequester; }

interface NeedsDemandRunner { demandRunner: DemandRunner };
interface NeedsDemandRunner { demandRunner: DemandRunner; }

interface Local { [k: string]: any; }

Expand Down Expand Up @@ -162,7 +162,11 @@ export function outstandingRequestId({ persistence }: NeedsPersistence) {
res.end();
}

return function outstandingRequestIdImpl(req: Request, res: Response, next: NextFunction) {
return function outstandingRequestIdImpl(
req: Request,
res: Response,
next: NextFunction,
) {
outstandingRequestIdSender(req, res)
.then(() => next())
.catch((err) => next(err));
Expand Down Expand Up @@ -215,12 +219,12 @@ export function runDemand({ serverVariableRequester, serviceInformation: { getAp

return async function runDemandImpl(req: Request, res: Response, next: NextFunction) {

let variables: EsqlateRequestCreationParameter;
let args: EsqlateArgument[];
try {
variables = getVariables(
args = getVariables(
{ getApiRoot },
serverVariableRequester,
req
req,
);
} catch (e) { return next(e); }

Expand All @@ -231,8 +235,8 @@ export function runDemand({ serverVariableRequester, serviceInformation: { getAp
req.params.definitionName,
);

// TODO: Pass in server variables propertly
demandRunner(definition, [], variables)
// TODO: Pass in server args propertly
demandRunner(definition, [], args)
.then((result: EsqlateResult) => {
res.json(result);
next();
Expand All @@ -243,26 +247,26 @@ export function runDemand({ serverVariableRequester, serviceInformation: { getAp

}

function getVariables({ getApiRoot }: ServiceInformation, serverVariableRequester: ServerVariableRequester, req: Request): EsqlateRequestCreationParameter {
function getVariables({ getApiRoot }: ServiceInformation, serverVariableRequester: ServerVariableRequester, req: Request): EsqlateArgument[] {

function get(req: Request): EsqlateRequestCreationParameter {
const serverVariables: EsqlateRequestCreationParameter = serverVariableRequester.listServerVariable(req).map(
(name) => ({ field_name: name, field_value: serverVariableRequester.getServerVariable(req, name) }),
function get(): EsqlateArgument[] {
const serverVariables: EsqlateArgument[] = serverVariableRequester.listServerVariable(req).map(
(name) => ({ name, value: serverVariableRequester.getServerVariable(req, name) }),
);

return serverVariables.reduce(
(acc: EsqlateRequestCreationParameter, sv) => {
const r = acc.filter((a) => !(("" + a.field_name) === ("" + sv.field_name)));
(acc: EsqlateArgument[], sv) => {
const r = acc.filter((a) => !(("" + a.name) === ("" + sv.name)));
r.push(sv);
return r;
},
req.body.concat([]),
(req.body as EsqlateRequestCreation).arguments.concat([]),
);
}

function getMissingVariables(definition: EsqlateDefinition, reqBody: EsqlateRequestCreationParameter) {
const available = new Set(reqBody.map((rb) => rb.field_name));
return definition.variables
function getMissingVariables(args: EsqlateArgument[]) {
const available = new Set(args.map((rb) => rb.name));
return definition.parameters
.filter((reqd) => {
return !available.has(reqd.name);
})
Expand All @@ -277,11 +281,10 @@ function getVariables({ getApiRoot }: ServiceInformation, serverVariableRequeste
"definitionName",
req.params.definitionName,
);
const variables = get(req);
const valid = ajvValidateRequest(variables);
const valid = ajvValidateRequestCreation(req.body);

if (!valid) {
const errors = ajvValidateRequest.errors;
const errors = ajvValidateRequestCreation.errors;
const msg = pathJoin(
getApiRoot(req),
"request",
Expand All @@ -290,7 +293,9 @@ function getVariables({ getApiRoot }: ServiceInformation, serverVariableRequeste
throw new EsqlateErrorInvalidRequestBody(msg);
}

const missingVariables = getMissingVariables(definition, variables);
const variables = get();

const missingVariables = getMissingVariables(variables);
if (missingVariables.length) {
const errorMsg = "Missing Variables: " + JSON.stringify(missingVariables);
const msg = pathJoin(
Expand All @@ -306,18 +311,18 @@ function getVariables({ getApiRoot }: ServiceInformation, serverVariableRequeste
}

const ajv = new Ajv();
const ajvValidateRequest = ajv.compile(schemaRequestCreation);
const ajvValidateRequestCreation = ajv.compile(schemaRequestCreation);

export function createRequest({ serverVariableRequester, persistence, queue, serviceInformation: { getApiRoot } }: NeedsPersistence & NeedsServiceInformation & NeedsQueue & NeedsServerVariableRequester) {

return function createRequestImpl(req: Request, res: Response, next: NextFunction) {

let variables: EsqlateRequestCreationParameter;
let variables: EsqlateArgument[];
try {
variables = getVariables(
{ getApiRoot },
serverVariableRequester,
req
req,
);
} catch (e) { return next(e); }

Expand Down
8 changes: 4 additions & 4 deletions src/persistence.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EsqlateDefinition, EsqlateRequestCreationParameter, EsqlateResult } from "esqlate-lib";
import { EsqlateArgument, EsqlateDefinition, EsqlateResult } from "esqlate-lib";
import { EsqlateErrorNotFoundPersistence } from "./logger";


Expand All @@ -20,7 +20,7 @@ export interface Persistence {
createRequest(
definitionName: EsqlateDefinition["name"],
requestId: RequestId,
values: EsqlateRequestCreationParameter): Promise<RequestId>;
values: EsqlateArgument[]): Promise<RequestId>;
getResult(definitionName: EsqlateDefinition["name"], resultId: ResultId): Promise<EsqlateResult>;
createResult(
definitionName: EsqlateDefinition["name"],
Expand All @@ -32,7 +32,7 @@ export type RequestId = string;
export type ResultId = string;

export interface RequestFileData {
params: EsqlateRequestCreationParameter;
params: EsqlateArgument[];
definition: string;
}

Expand Down Expand Up @@ -84,7 +84,7 @@ export class FilesystemPersistence implements Persistence {
public createRequest(
definitionName: EsqlateDefinition["name"],
requestId: RequestId,
values: EsqlateRequestCreationParameter): Promise<RequestId> {
values: EsqlateArgument[]): Promise<RequestId> {

assert(requestId.length === this.pathSeperator2, `Request Ids must be ${this.pathSeperator2} characters long`);

Expand Down
Loading

0 comments on commit 73af901

Please sign in to comment.