diff --git a/__tests__/helpers.js b/__tests__/helpers.js index f5ef768..d28e483 100644 --- a/__tests__/helpers.js +++ b/__tests__/helpers.js @@ -8,34 +8,34 @@ import * as rds from "../rds"; let client = null; const runResolverFunctionOnAWS = async (code, context, functionName) => { - if (!client) { - client = new AppSyncClient(); - } - - if (!context) { - context = {}; - } - - const command = new EvaluateCodeCommand({ - code, - context: JSON.stringify(context), - function: functionName, - runtime: { - name: "APPSYNC_JS", - runtimeVersion: "1.0.0", - }, - }); - - const result = await client.send(command); - try { - return JSON.parse(result.evaluationResult); - } catch (e) { - console.error("invalid json", result); - } + if (!client) { + client = new AppSyncClient(); + } + + if (!context) { + context = {}; + } + + const command = new EvaluateCodeCommand({ + code, + context: JSON.stringify(context), + function: functionName, + runtime: { + name: "APPSYNC_JS", + runtimeVersion: "1.0.0", + }, + }); + + const result = await client.send(command); + try { + return JSON.parse(result.evaluationResult); + } catch (e) { + console.error("invalid json", result); + } }; const runOnAWS = async (s, context) => { - const code = ` + const code = ` import { util } from '@aws-appsync/utils'; import * as ddb from '@aws-appsync/utils/dynamodb'; import * as rds from '@aws-appsync/utils/rds'; @@ -48,50 +48,50 @@ const runOnAWS = async (s, context) => { } `; - return await runResolverFunctionOnAWS(code, context, "request"); + return await runResolverFunctionOnAWS(code, context, "request"); } export const checkResolverValid = async (code, context, functionName) => { - let result; - if (process.env.TEST_TARGET === "AWS_CLOUD") { - const fullCode = ` + let result; + if (process.env.TEST_TARGET === "AWS_CLOUD") { + const fullCode = ` import { util } from '@aws-appsync/utils'; import * as rds from '@aws-appsync/utils/rds'; ` + code; - result = await runResolverFunctionOnAWS(fullCode, context, functionName); - } else { - const fullCode = ` + result = await runResolverFunctionOnAWS(fullCode, context, functionName); + } else { + const fullCode = ` import { util } from '..'; import * as rds from '../rds'; ` + code; - const encodedJs = encodeURIComponent(fullCode); - const dataUri = 'data:text/javascript;charset=utf-8,' + encodedJs; - const module = await import(dataUri); + const encodedJs = encodeURIComponent(fullCode); + const dataUri = 'data:text/javascript;charset=utf-8,' + encodedJs; + const module = await import(dataUri); - const fn = module[functionName]; + const fn = module[functionName]; - transformContextForAppSync(context); - result = fn(context); - } - expect(result).toMatchSnapshot(); + transformContextForAppSync(context); + result = fn(context); + } + expect(result).toMatchSnapshot(); }; // manipulate the context object to behave like the AppSync equivalent function transformContextForAppSync(context) { - context.args = context.arguments; + context.args = context.arguments; } // If TEST_TARGET is AWS_CLOUD then run the check against AWS. Otherwise, run locally. export const checkValid = async (s, context, postProcess) => { - let result; - if (process.env.TEST_TARGET === "AWS_CLOUD") { - result = await runOnAWS(s, context); - } else { - result = eval(s); - } - if (postProcess) { - result = postProcess(result); - } - expect(result).toMatchSnapshot(); + let result; + if (process.env.TEST_TARGET === "AWS_CLOUD") { + result = await runOnAWS(s, context); + } else { + result = eval(s); + } + if (postProcess) { + result = postProcess(result); + } + expect(result).toMatchSnapshot(); } diff --git a/__tests__/index.test.js b/__tests__/index.test.js index 972a6f9..37f7c5d 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -10,205 +10,205 @@ import { util } from ".."; describe("general utilities", () => { - test("autoId", async () => { - // cannot test on AWS due to random nature - expect(util.autoId()).toBeTruthy(); - }); + test("autoId", async () => { + // cannot test on AWS due to random nature + expect(util.autoId()).toBeTruthy(); + }); }); describe("time utilities", () => { - test("nowFormatted", async () => { - // patch date utilities to ensure consistency - const newDate = new Date(2021, 1, 1); - const spied = jest.spyOn(global, 'Date').mockImplementation(() => newDate); - - // TODO: not strictly correct, but close - expect(util.time.nowFormatted('YYYY-MM-dd HH:mm:ss')).toEqual("2021-02-01T00:00:00.000Z"); - spied.mockRestore(); - }); + test("nowFormatted", async () => { + // patch date utilities to ensure consistency + const newDate = new Date(2021, 1, 1); + const spied = jest.spyOn(global, 'Date').mockImplementation(() => newDate); + + // TODO: not strictly correct, but close + expect(util.time.nowFormatted('YYYY-MM-dd HH:mm:ss')).toEqual("2021-02-01T00:00:00.000Z"); + spied.mockRestore(); + }); }); describe("dynamodb helpers", () => { - describe("toDynamoDB", () => { - test("string", async () => { - await checkValid(`util.dynamodb.toDynamoDB("test")`); - }); - test("number", async () => { - await checkValid(`util.dynamodb.toDynamoDB(12345)`); - }); - test("boolean", async () => { - await checkValid(`util.dynamodb.toDynamoDB(true)`); - }); + describe("toDynamoDB", () => { + test("string", async () => { + await checkValid(`util.dynamodb.toDynamoDB("test")`); }); - - test("toString", async () => { - await checkValid(`util.dynamodb.toString("test")`); + test("number", async () => { + await checkValid(`util.dynamodb.toDynamoDB(12345)`); }); - - test("toStringSet", async () => { - await checkValid(`util.dynamodb.toStringSet(["foo", "bar", "baz"])`); + test("boolean", async () => { + await checkValid(`util.dynamodb.toDynamoDB(true)`); }); + }); - test("toNumber", async () => { - await checkValid(`util.dynamodb.toNumber(12345)`); - }); + test("toString", async () => { + await checkValid(`util.dynamodb.toString("test")`); + }); - test("toNumberSet", async () => { - await checkValid(`util.dynamodb.toNumberSet([1, 23, 4.56])`); - }); + test("toStringSet", async () => { + await checkValid(`util.dynamodb.toStringSet(["foo", "bar", "baz"])`); + }); - test("toBinary", async () => { - await checkValid(`util.dynamodb.toBinary("foo")`); - }); + test("toNumber", async () => { + await checkValid(`util.dynamodb.toNumber(12345)`); + }); - test("toBinarySet", async () => { - await checkValid(`util.dynamodb.toBinarySet(["foo", "bar", "baz"])`); - }); + test("toNumberSet", async () => { + await checkValid(`util.dynamodb.toNumberSet([1, 23, 4.56])`); + }); - test("toBoolean", async () => { - await checkValid(`util.dynamodb.toBoolean(true)`); - }); + test("toBinary", async () => { + await checkValid(`util.dynamodb.toBinary("foo")`); + }); - test("toNull", async () => { - await checkValid(`util.dynamodb.toNull()`); - }); + test("toBinarySet", async () => { + await checkValid(`util.dynamodb.toBinarySet(["foo", "bar", "baz"])`); + }); - test("toList", async () => { - await checkValid(`util.dynamodb.toList(["foo", 123, {bar: "baz" }])`); - }); + test("toBoolean", async () => { + await checkValid(`util.dynamodb.toBoolean(true)`); + }); - test("toMap", async () => { - await checkValid(`util.dynamodb.toMap({ "foo": "bar", "baz": 1234, "beep": ["boop"] })`); - }); + test("toNull", async () => { + await checkValid(`util.dynamodb.toNull()`); + }); - test("toMapValues", async () => { - await checkValid(`util.dynamodb.toMapValues({ "foo": "bar", "baz": 1234, "beep": ["boop"] })`); - }); + test("toList", async () => { + await checkValid(`util.dynamodb.toList(["foo", 123, {bar: "baz" }])`); + }); - describe("s3 objects", () => { - test("three parameter function", async () => { - await checkValid(`util.dynamodb.toS3Object("foo", "bar", "baz")`); - }); - test("four parameter function", async () => { - await checkValid(`util.dynamodb.toS3Object("foo", "bar", "baz", "beep")`); - }); - test.skip("fromS3ObjectJson", async () => { - await checkValid(`util.dynamodb.fromS3ObjectJson({ "S" : "{ \"s3\" : { \"key\" : \"foo\", \"bucket\" : \"bar\", \"region\" : \"baz\", \"version\" = \"beep\" } }" })`); - }); - }); -}); + test("toMap", async () => { + await checkValid(`util.dynamodb.toMap({ "foo": "bar", "baz": 1234, "beep": ["boop"] })`); + }); -describe("DynamoDB module functions", () => { - test("get", async () => { - await checkValid(`ddb.get({ key: { id: "id" }})`); - }); + test("toMapValues", async () => { + await checkValid(`util.dynamodb.toMapValues({ "foo": "bar", "baz": 1234, "beep": ["boop"] })`); + }); - test("put", async () => { - await checkValid(`ddb.put({ key: { id: "abc" }, item: { value: 10 }})`); + describe("s3 objects", () => { + test("three parameter function", async () => { + await checkValid(`util.dynamodb.toS3Object("foo", "bar", "baz")`); }); - - test("remove", async () => { - await checkValid(`ddb.remove({ key: { id: "test" } })`); + test("four parameter function", async () => { + await checkValid(`util.dynamodb.toS3Object("foo", "bar", "baz", "beep")`); }); - - test("scan", async () => { - await checkValid(`ddb.scan({ limit: 10, nextToken: "abc"})`); - }); - - // Not implemented on AWS - // Error: code.js(5,14): error TS2339: Property 'sync' does not exist on type 'typeof import("/var/task/node_modules/@amzn/awsapp-sync-jsvtltranspiler/bundled/@aws-appsync/utils/lib/dynamo-db-helpers")'. - test.skip("sync", async () => { - await checkValid(`ddb.sync({ limit: 10, nextToken: "abc", lastSync: 1 })`); + test.skip("fromS3ObjectJson", async () => { + await checkValid(`util.dynamodb.fromS3ObjectJson({ "S" : "{ \"s3\" : { \"key\" : \"foo\", \"bucket\" : \"bar\", \"region\" : \"baz\", \"version\" = \"beep\" } }" })`); }); + }); +}); - describe("update", () => { - test("add", async () => { - await checkValid(`ddb.update({ key: { id: "test" }, update: { age: ddb.operations.add(10), } })`); - }); - - test("append", async () => { - await checkValid(`ddb.update({ key: { id: "test" }, update: { values: ddb.operations.append([1, 2, 3]), } })`); - }); - - test("decrement", async () => { - await checkValid(`ddb.update({ key: { id: "test" }, update: { age: ddb.operations.decrement(10) } })`); - }); +describe("DynamoDB module functions", () => { + test("get", async () => { + await checkValid(`ddb.get({ key: { id: "id" }})`); + }); - test("increment", async () => { - await checkValid(`ddb.update({ key: { id: "test" }, update: { age: ddb.operations.increment(10) } })`); - }); + test("put", async () => { + await checkValid(`ddb.put({ key: { id: "abc" }, item: { value: 10 }})`); + }); - test("prepend", async () => { - await checkValid(`ddb.update({ key: { id: "test" }, update: { values: ddb.operations.prepend([1, 2, 3]) } })`); - }); + test("remove", async () => { + await checkValid(`ddb.remove({ key: { id: "test" } })`); + }); - test("replace", async () => { - await checkValid(`ddb.update({ key: { id: "test" }, update: { values: ddb.operations.replace({ a: 10 }) }})`); - }); - }); -}) + test("scan", async () => { + await checkValid(`ddb.scan({ limit: 10, nextToken: "abc"})`); + }); -describe("Transformations", () => { - test("toDynamoDBFilterMap", async () => { - await checkValid(`util.transform.toDynamoDBFilterExpression({ "title":{ "contains":"Hello World" } })`); - }); - - test("toDynamoDBConditionExpression", async () => { - // attribute keys are not guaranteed to be ordered - const postProcess = (result) => { - - const sortObjectByKeys = (obj) => { - return Object.keys(obj).sort().reduce( - (res, key) => { - res[key] = obj[key]; - return res; - }, - {} - ); - }; - - const { expression, expressionNames, expressionValues } = JSON.parse(result); - const transformed = { - expression, - expressionNames: sortObjectByKeys(expressionNames), - expressionValues: sortObjectByKeys(expressionValues), - }; - return JSON.stringify(transformed); - }; - await checkValid(`util.transform.toDynamoDBConditionExpression({ - id: { attributeExists: true }, - version: { eq: 10 }, - })`, {}, postProcess); - }); -}); + // Not implemented on AWS + // Error: code.js(5,14): error TS2339: Property 'sync' does not exist on type 'typeof import("/var/task/node_modules/@amzn/awsapp-sync-jsvtltranspiler/bundled/@aws-appsync/utils/lib/dynamo-db-helpers")'. + test.skip("sync", async () => { + await checkValid(`ddb.sync({ limit: 10, nextToken: "abc", lastSync: 1 })`); + }); -describe("DynamoDB operations", () => { + describe("update", () => { test("add", async () => { - await checkValid(`ddb.operations.add(10)`); + await checkValid(`ddb.update({ key: { id: "test" }, update: { age: ddb.operations.add(10), } })`); }); test("append", async () => { - await checkValid(`ddb.operations.append([1, 2, 3])`); + await checkValid(`ddb.update({ key: { id: "test" }, update: { values: ddb.operations.append([1, 2, 3]), } })`); }); test("decrement", async () => { - await checkValid(`ddb.operations.decrement(10)`); + await checkValid(`ddb.update({ key: { id: "test" }, update: { age: ddb.operations.decrement(10) } })`); }); test("increment", async () => { - await checkValid(`ddb.operations.increment(10)`); + await checkValid(`ddb.update({ key: { id: "test" }, update: { age: ddb.operations.increment(10) } })`); }); test("prepend", async () => { - await checkValid(`ddb.operations.prepend([1, 2, 3])`); + await checkValid(`ddb.update({ key: { id: "test" }, update: { values: ddb.operations.prepend([1, 2, 3]) } })`); }); test("replace", async () => { - await checkValid(`ddb.operations.replace({ a: 10 })`); + await checkValid(`ddb.update({ key: { id: "test" }, update: { values: ddb.operations.replace({ a: 10 }) }})`); }); + }); +}) - // not implemented currently - test.skip("updateListItem", async () => { - await checkValid(`ddb.operations.updateListItem('foo', 1)`); - }); +describe("Transformations", () => { + test("toDynamoDBFilterMap", async () => { + await checkValid(`util.transform.toDynamoDBFilterExpression({ "title":{ "contains":"Hello World" } })`); + }); + + test("toDynamoDBConditionExpression", async () => { + // attribute keys are not guaranteed to be ordered + const postProcess = (result) => { + + const sortObjectByKeys = (obj) => { + return Object.keys(obj).sort().reduce( + (res, key) => { + res[key] = obj[key]; + return res; + }, + {} + ); + }; + + const { expression, expressionNames, expressionValues } = JSON.parse(result); + const transformed = { + expression, + expressionNames: sortObjectByKeys(expressionNames), + expressionValues: sortObjectByKeys(expressionValues), + }; + return JSON.stringify(transformed); + }; + await checkValid(`util.transform.toDynamoDBConditionExpression({ + id: { attributeExists: true }, + version: { eq: 10 }, + })`, {}, postProcess); + }); +}); + +describe("DynamoDB operations", () => { + test("add", async () => { + await checkValid(`ddb.operations.add(10)`); + }); + + test("append", async () => { + await checkValid(`ddb.operations.append([1, 2, 3])`); + }); + + test("decrement", async () => { + await checkValid(`ddb.operations.decrement(10)`); + }); + + test("increment", async () => { + await checkValid(`ddb.operations.increment(10)`); + }); + + test("prepend", async () => { + await checkValid(`ddb.operations.prepend([1, 2, 3])`); + }); + + test("replace", async () => { + await checkValid(`ddb.operations.replace({ a: 10 })`); + }); + + // not implemented currently + test.skip("updateListItem", async () => { + await checkValid(`ddb.operations.updateListItem('foo', 1)`); + }); }); diff --git a/__tests__/resolvers.test.js b/__tests__/resolvers.test.js index b387fc4..5f88304 100644 --- a/__tests__/resolvers.test.js +++ b/__tests__/resolvers.test.js @@ -7,8 +7,8 @@ import { checkResolverValid } from "./helpers"; import { util } from ".."; describe("dynamodb resolvers", () => { - test("something", async () => { - const code = ` + test("something", async () => { + const code = ` export function request(ctx) { return { operation: "Query", @@ -40,33 +40,33 @@ describe("dynamodb resolvers", () => { } `; - const requestContext = { - arguments: { - filter: { - line: "test", - shift: 10, - }, - }, - }; - - await checkResolverValid(code, requestContext, "request"); - - const responseContext = { - result: { - items: [ - { a: 10 }, - ], - }, - }; - - await checkResolverValid(code, responseContext, "response"); - }); + const requestContext = { + arguments: { + filter: { + line: "test", + shift: 10, + }, + }, + }; + + await checkResolverValid(code, requestContext, "request"); + + const responseContext = { + result: { + items: [ + { a: 10 }, + ], + }, + }; + + await checkResolverValid(code, responseContext, "response"); + }); }); describe("rds resolvers", () => { - describe("typehints", () => { - test("UUID", async () => { - const code = ` + describe("typehints", () => { + test("UUID", async () => { + const code = ` export function request(ctx) { return rds.typeHint.UUID(ctx.args.id); } @@ -75,17 +75,17 @@ describe("rds resolvers", () => { } `; - const context = { - arguments: { - id: "abc123", - }, - }; + const context = { + arguments: { + id: "abc123", + }, + }; - await checkResolverValid(code, context, "request"); - }); + await checkResolverValid(code, context, "request"); + }); - test("TIMESTAMP", async () => { - const code = ` + test("TIMESTAMP", async () => { + const code = ` export function request(ctx) { return rds.typeHint.TIMESTAMP(ctx.args.id); } @@ -94,103 +94,103 @@ describe("rds resolvers", () => { } `; - const context = { - arguments: { - id: new Date(2023, 1, 1), - }, - }; + const context = { + arguments: { + id: new Date(2023, 1, 1), + }, + }; - await checkResolverValid(code, context, "request"); - }); + await checkResolverValid(code, context, "request"); }); - - // https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-rds-js.html - test("toJsonObject", async () => { - const responseContext = { - "result": JSON.stringify({ - "sqlStatementResults": [ - { - "numberOfRecordsUpdated": 0, - "records": [ - [ - { - "stringValue": "Mark Twain" - }, - { - "stringValue": "Adventures of Huckleberry Finn" - }, - { - "stringValue": "978-1948132817" - } - ], - [ - { - "stringValue": "Jack London" - }, - { - "stringValue": "The Call of the Wild" - }, - { - "stringValue": "978-1948132275" - } - ] - ], - "columnMetadata": [ - { - "isSigned": false, - "isCurrency": false, - "label": "author", - "precision": 200, - "typeName": "VARCHAR", - "scale": 0, - "isAutoIncrement": false, - "isCaseSensitive": false, - "schemaName": "", - "tableName": "Books", - "type": 12, - "nullable": 0, - "arrayBaseColumnType": 0, - "name": "author" - }, - { - "isSigned": false, - "isCurrency": false, - "label": "title", - "precision": 200, - "typeName": "VARCHAR", - "scale": 0, - "isAutoIncrement": false, - "isCaseSensitive": false, - "schemaName": "", - "tableName": "Books", - "type": 12, - "nullable": 0, - "arrayBaseColumnType": 0, - "name": "title" - }, - { - "isSigned": false, - "isCurrency": false, - "label": "ISBN-13", - "precision": 15, - "typeName": "VARCHAR", - "scale": 0, - "isAutoIncrement": false, - "isCaseSensitive": false, - "schemaName": "", - "tableName": "Books", - "type": 12, - "nullable": 0, - "arrayBaseColumnType": 0, - "name": "ISBN-13" - } - ] - } - ] - }), - }; - - const code = ` + }); + + // https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-rds-js.html + test("toJsonObject", async () => { + const responseContext = { + "result": JSON.stringify({ + "sqlStatementResults": [ + { + "numberOfRecordsUpdated": 0, + "records": [ + [ + { + "stringValue": "Mark Twain" + }, + { + "stringValue": "Adventures of Huckleberry Finn" + }, + { + "stringValue": "978-1948132817" + } + ], + [ + { + "stringValue": "Jack London" + }, + { + "stringValue": "The Call of the Wild" + }, + { + "stringValue": "978-1948132275" + } + ] + ], + "columnMetadata": [ + { + "isSigned": false, + "isCurrency": false, + "label": "author", + "precision": 200, + "typeName": "VARCHAR", + "scale": 0, + "isAutoIncrement": false, + "isCaseSensitive": false, + "schemaName": "", + "tableName": "Books", + "type": 12, + "nullable": 0, + "arrayBaseColumnType": 0, + "name": "author" + }, + { + "isSigned": false, + "isCurrency": false, + "label": "title", + "precision": 200, + "typeName": "VARCHAR", + "scale": 0, + "isAutoIncrement": false, + "isCaseSensitive": false, + "schemaName": "", + "tableName": "Books", + "type": 12, + "nullable": 0, + "arrayBaseColumnType": 0, + "name": "title" + }, + { + "isSigned": false, + "isCurrency": false, + "label": "ISBN-13", + "precision": 15, + "typeName": "VARCHAR", + "scale": 0, + "isAutoIncrement": false, + "isCaseSensitive": false, + "schemaName": "", + "tableName": "Books", + "type": 12, + "nullable": 0, + "arrayBaseColumnType": 0, + "name": "ISBN-13" + } + ] + } + ] + }), + }; + + const code = ` export function request(ctx) {} export function response(ctx) { @@ -198,11 +198,11 @@ describe("rds resolvers", () => { } `; - await checkResolverValid(code, responseContext, "response"); - }); + await checkResolverValid(code, responseContext, "response"); + }); - test("createPgStatement-typeHint", async () => { - const code = ` + test("createPgStatement-typeHint", async () => { + const code = ` export function request(ctx) { const whereClause = { and:[ { id: { eq: rds.typeHint.UUID(ctx.args.id) } }, @@ -216,20 +216,20 @@ describe("rds resolvers", () => { export function response(ctx) {} ` - const requestContext = { - arguments: { - id: "1232", - name: "hello", - started: new Date(2022, 2, 2), - } - }; + const requestContext = { + arguments: { + id: "1232", + name: "hello", + started: new Date(2022, 2, 2), + } + }; - await checkResolverValid(code, requestContext, "request"); + await checkResolverValid(code, requestContext, "request"); - }); + }); - test("createPgStatement-select", async () => { - const code = ` + test("createPgStatement-select", async () => { + const code = ` export function request(ctx) { const whereClause = { or: [ { name: { eq: 'Stephane'} }, @@ -248,14 +248,14 @@ describe("rds resolvers", () => { export function response(ctx) {} `; - const requestContext = {}; + const requestContext = {}; - await checkResolverValid(code, requestContext, "request"); + await checkResolverValid(code, requestContext, "request"); - }); + }); - test("createPgStatement-remove", async () => { - const code = ` + test("createPgStatement-remove", async () => { + const code = ` export function request(ctx) { const id = ctx.args.id; const where = { id: { eq: id } }; @@ -270,14 +270,14 @@ describe("rds resolvers", () => { export function response(ctx) {} `; - const requestContext = { - arguments: { - id: "1232" - } - }; + const requestContext = { + arguments: { + id: "1232" + } + }; - await checkResolverValid(code, requestContext, "request"); + await checkResolverValid(code, requestContext, "request"); - }); + }); }); diff --git a/dynamodb/index.js b/dynamodb/index.js index e07e943..1464ef8 100644 --- a/dynamodb/index.js +++ b/dynamodb/index.js @@ -130,22 +130,22 @@ export const update = (payload) => { export const operations = { add: (value) => { - return {type: OPERATION_ADD, value: value}; + return { type: OPERATION_ADD, value: value }; }, append: (value) => { - return {type: OPERATION_APPEND, items: value}; + return { type: OPERATION_APPEND, items: value }; }, decrement: (value) => { - return {type: OPERATION_DECREMENT, by: value}; + return { type: OPERATION_DECREMENT, by: value }; }, increment: (value) => { - return {type: OPERATION_INCREMENT, by: value}; + return { type: OPERATION_INCREMENT, by: value }; }, prepend: (value) => { - return {type: OPERATION_PREPEND, items: value}; + return { type: OPERATION_PREPEND, items: value }; }, replace: (value) => { - return {type: OPERATION_REPLACE, value: value}; + return { type: OPERATION_REPLACE, value: value }; }, // updateListItem: (value) => { // return {type: OPERATION_UPDATE_LIST_ITEM, value: value}; diff --git a/index.js b/index.js index 2a01b0d..5cae6f6 100644 --- a/index.js +++ b/index.js @@ -1,340 +1,340 @@ import { v4 as uuidv4 } from 'uuid'; export const dynamodbUtils = { - toDynamoDB: function(value) { - if (typeof (value) === "number") { - return this.toNumber(value); - } else if (typeof (value) === "string") { - return this.toString(value); - } else if (typeof (value) === "boolean") { - return this.toBoolean(value); - } else if (typeof (value) === "object") { - if (value.length !== undefined) { - return this.toList(value); - } else { - return this.toMap(value); - } - } else { - throw new Error(`Not implemented for ${value}`); - } - }, + toDynamoDB: function(value) { + if (typeof (value) === "number") { + return this.toNumber(value); + } else if (typeof (value) === "string") { + return this.toString(value); + } else if (typeof (value) === "boolean") { + return this.toBoolean(value); + } else if (typeof (value) === "object") { + if (value.length !== undefined) { + return this.toList(value); + } else { + return this.toMap(value); + } + } else { + throw new Error(`Not implemented for ${value}`); + } + }, - toString: function(value) { - if (value === null) { return null; }; + toString: function(value) { + if (value === null) { return null; }; - return { S: value }; - }, + return { S: value }; + }, - toStringSet: function(value) { - if (value === null) { return null; }; + toStringSet: function(value) { + if (value === null) { return null; }; - return { SS: value }; - }, + return { SS: value }; + }, - toNumber: function(value) { - if (value === null) { return null; }; + toNumber: function(value) { + if (value === null) { return null; }; - return { N: value }; - }, + return { N: value }; + }, - toNumberSet: function(value) { - if (value === null) { return null; }; + toNumberSet: function(value) { + if (value === null) { return null; }; - return { NS: value }; - }, + return { NS: value }; + }, - toBinary: function(value) { - if (value === null) { return null; }; + toBinary: function(value) { + if (value === null) { return null; }; - return { B: value }; - }, + return { B: value }; + }, - toBinarySet: function(value) { - if (value === null) { return null; }; + toBinarySet: function(value) { + if (value === null) { return null; }; - return { BS: value }; - }, + return { BS: value }; + }, - toBoolean: function(value) { - if (value === null) { return null; }; + toBoolean: function(value) { + if (value === null) { return null; }; - return { BOOL: value }; - }, + return { BOOL: value }; + }, - toNull: function() { - return { NULL: null }; - }, + toNull: function() { + return { NULL: null }; + }, - toList: function(values) { - let out = []; - for (const value of values) { - out.push(this.toDynamoDB(value)); - } - return { L: out } - }, + toList: function(values) { + let out = []; + for (const value of values) { + out.push(this.toDynamoDB(value)); + } + return { L: out } + }, - toMap: function(mapping) { - return { M: this.toMapValues(mapping) }; - }, + toMap: function(mapping) { + return { M: this.toMapValues(mapping) }; + }, - toMapValues: function(mapping) { - let out = {}; - for (const [k, v] of Object.entries(mapping)) { - out[k] = this.toDynamoDB(v); + toMapValues: function(mapping) { + let out = {}; + for (const [k, v] of Object.entries(mapping)) { + out[k] = this.toDynamoDB(v); + } + return out; + }, + + toS3Object: function(key, bucket, region, version) { + let payload; + if (version === undefined) { + payload = { + s3: { + key, + bucket, + region, } - return out; - }, - - toS3Object: function(key, bucket, region, version) { - let payload; - if (version === undefined) { - payload = { - s3: { - key, - bucket, - region, - } - }; - } else { - payload = { - s3: { - key, - bucket, - region, - version, - } - }; - }; - return this.toString(JSON.stringify(payload)); - }, + }; + } else { + payload = { + s3: { + key, + bucket, + region, + version, + } + }; + }; + return this.toString(JSON.stringify(payload)); + }, - fromS3ObjectJson: function(value) { - throw new Error("not implemented"); - }, + fromS3ObjectJson: function(value) { + throw new Error("not implemented"); + }, } const FILTER_CONTAINS = "contains"; export const util = { - autoId: function() { - return uuidv4(); + autoId: function() { + return uuidv4(); + }, + time: { + nowFormatted: function(pattern) { + // TODO: not completely correct, but close enough probably + return new Date().toISOString(); }, - time: { - nowFormatted: function(pattern) { - // TODO: not completely correct, but close enough probably - return new Date().toISOString(); - }, + }, + transform: { + toDynamoDBFilterExpression: function(value) { + const items = Object.entries(value); + if (items.length != 1) { + throw new Error("invalid structure, should have one entry"); + } + + const [key, filter] = items[0]; + + const filterItems = Object.entries(filter); + if (filterItems.length !== 1) { + throw new Error("invalid structure, should have one filter expression"); + } + + + const [filterType, contents] = filterItems[0]; + const expressionName = `#${key}`; + const expressionValue = `:${key}_${filterType}`; + + let expression; + let expressionNames = {}; + let expressionValues = {}; + switch (filterType) { + case FILTER_CONTAINS: + expression = `(contains(${expressionName},${expressionValue}))`; + expressionNames[expressionName] = key; + expressionValues[expressionValue] = util.dynamodb.toDynamoDB(contents); + break; + default: + throw new Error(`Not implemented for ${filterType}`); + + } + + return JSON.stringify({ expression, expressionNames, expressionValues }); + }, - transform: { - toDynamoDBFilterExpression: function(value) { - const items = Object.entries(value); - if (items.length != 1) { - throw new Error("invalid structure, should have one entry"); - } - - const [key, filter] = items[0]; - - const filterItems = Object.entries(filter); - if (filterItems.length !== 1) { - throw new Error("invalid structure, should have one filter expression"); - } - - - const [filterType, contents] = filterItems[0]; - const expressionName = `#${key}`; - const expressionValue = `:${key}_${filterType}`; - - let expression; - let expressionNames = {}; - let expressionValues = {}; - switch (filterType) { - case FILTER_CONTAINS: - expression = `(contains(${expressionName},${expressionValue}))`; - expressionNames[expressionName] = key; - expressionValues[expressionValue] = util.dynamodb.toDynamoDB(contents); - break; - default: - throw new Error(`Not implemented for ${filterType}`); - - } - - return JSON.stringify({ expression, expressionNames, expressionValues }); - - }, - toDynamoDBConditionExpression(condition) { - const result = generateFilterExpression(condition); - return JSON.stringify({ - expression: result.expressions.join(' ').trim(), - expressionNames: result.expressionNames, - // upstream is missing this value: https://github.com/aws-amplify/amplify-cli/blob/5cc1b556d8081421dc68ee264dac02d5660ffee7/packages/amplify-appsync-simulator/src/velocity/util/transform/index.ts#L11 - expressionValues: result.expressionValues, - }); - }, + toDynamoDBConditionExpression(condition) { + const result = generateFilterExpression(condition); + return JSON.stringify({ + expression: result.expressions.join(' ').trim(), + expressionNames: result.expressionNames, + // upstream is missing this value: https://github.com/aws-amplify/amplify-cli/blob/5cc1b556d8081421dc68ee264dac02d5660ffee7/packages/amplify-appsync-simulator/src/velocity/util/transform/index.ts#L11 + expressionValues: result.expressionValues, + }); }, - dynamodb: dynamodbUtils, + }, + dynamodb: dynamodbUtils, }; // embedded here because imports don't yet work const OPERATOR_MAP = { - ne: '<>', - eq: '=', - lt: '<', - le: '<=', - gt: '>', - ge: '>=', - in: 'contains', + ne: '<>', + eq: '=', + lt: '<', + le: '<=', + gt: '>', + ge: '>=', + in: 'contains', }; const FUNCTION_MAP = { - contains: 'contains', - notContains: 'NOT contains', - beginsWith: 'begins_with', + contains: 'contains', + notContains: 'NOT contains', + beginsWith: 'begins_with', }; export function generateFilterExpression(filter, prefix, parent) { - const expr = Object.entries(filter).reduce( - (sum, [name, value]) => { - let subExpr = { - expressions: [], - expressionNames: {}, - expressionValues: {}, - }; - const fieldName = createExpressionFieldName(parent); - const filedValueName = createExpressionValueName(parent, name, prefix); - - switch (name) { - case 'or': - case 'and': { - const JOINER = name === 'or' ? 'OR' : 'AND'; - if (Array.isArray(value)) { - subExpr = scopeExpression( - value.reduce((expr, subFilter, idx) => { - const newExpr = generateFilterExpression(subFilter, [prefix, name, idx].filter((i) => i !== null).join('_')); - return merge(expr, newExpr, JOINER); - }, subExpr), - ); - } else { - subExpr = generateFilterExpression(value, [prefix, name].filter((val) => val !== null).join('_')); - } - break; - } - case 'not': { - subExpr = scopeExpression(generateFilterExpression(value, [prefix, name].filter((val) => val !== null).join('_'))); - subExpr.expressions.unshift('NOT'); - break; - } - case 'between': { - const expr1 = createExpressionValueName(parent, 'between_1', prefix); - const expr2 = createExpressionValueName(parent, 'between_2', prefix); - const exprName = createExpressionName(parent); - const subExprExpr = `${createExpressionFieldName(parent)} BETWEEN ${expr1} AND ${expr2}`; - const exprValues = { - ...createExpressionValue(parent, 'between_1', value[0], prefix), - ...createExpressionValue(parent, 'between_2', value[1], prefix), - }; - subExpr = { - expressions: [subExprExpr], - expressionNames: exprName, - expressionValues: exprValues, - }; - break; - } - case 'ne': - case 'eq': - case 'gt': - case 'ge': - case 'lt': - case 'le': { - const operator = OPERATOR_MAP[name]; - subExpr = { - expressions: [`(${fieldName} ${operator} ${filedValueName})`], - expressionNames: createExpressionName(parent), - expressionValues: createExpressionValue(parent, name, value, prefix), - }; - break; - } - case 'attributeExists': { - const existsName = value === true ? 'attribute_exists' : 'attribute_not_exists'; - subExpr = { - expressions: [`(${existsName}(${fieldName}))`], - expressionNames: createExpressionName(parent), - expressionValues: [], - }; - break; - } - case 'contains': - case 'notContains': - case 'beginsWith': { - const functionName = FUNCTION_MAP[name]; - subExpr = { - expressions: [`(${functionName}(${fieldName}, ${filedValueName}))`], - expressionNames: createExpressionName(parent), - expressionValues: createExpressionValue(parent, name, value, prefix), - }; - break; - } - case 'in': { - const operatorName = OPERATOR_MAP[name]; - subExpr = { - expressions: [`(${operatorName}(${filedValueName}, ${fieldName}))`], - expressionNames: createExpressionName(parent), - expressionValues: createExpressionValue(parent, name, value, prefix), - }; - break; - } - default: - subExpr = scopeExpression(generateFilterExpression(value, prefix, name)); - } - return merge(sum, subExpr); - }, - { - expressions: [], - expressionNames: {}, - expressionValues: {}, - }, - ); - - return expr; + const expr = Object.entries(filter).reduce( + (sum, [name, value]) => { + let subExpr = { + expressions: [], + expressionNames: {}, + expressionValues: {}, + }; + const fieldName = createExpressionFieldName(parent); + const filedValueName = createExpressionValueName(parent, name, prefix); + + switch (name) { + case 'or': + case 'and': { + const JOINER = name === 'or' ? 'OR' : 'AND'; + if (Array.isArray(value)) { + subExpr = scopeExpression( + value.reduce((expr, subFilter, idx) => { + const newExpr = generateFilterExpression(subFilter, [prefix, name, idx].filter((i) => i !== null).join('_')); + return merge(expr, newExpr, JOINER); + }, subExpr), + ); + } else { + subExpr = generateFilterExpression(value, [prefix, name].filter((val) => val !== null).join('_')); + } + break; + } + case 'not': { + subExpr = scopeExpression(generateFilterExpression(value, [prefix, name].filter((val) => val !== null).join('_'))); + subExpr.expressions.unshift('NOT'); + break; + } + case 'between': { + const expr1 = createExpressionValueName(parent, 'between_1', prefix); + const expr2 = createExpressionValueName(parent, 'between_2', prefix); + const exprName = createExpressionName(parent); + const subExprExpr = `${createExpressionFieldName(parent)} BETWEEN ${expr1} AND ${expr2}`; + const exprValues = { + ...createExpressionValue(parent, 'between_1', value[0], prefix), + ...createExpressionValue(parent, 'between_2', value[1], prefix), + }; + subExpr = { + expressions: [subExprExpr], + expressionNames: exprName, + expressionValues: exprValues, + }; + break; + } + case 'ne': + case 'eq': + case 'gt': + case 'ge': + case 'lt': + case 'le': { + const operator = OPERATOR_MAP[name]; + subExpr = { + expressions: [`(${fieldName} ${operator} ${filedValueName})`], + expressionNames: createExpressionName(parent), + expressionValues: createExpressionValue(parent, name, value, prefix), + }; + break; + } + case 'attributeExists': { + const existsName = value === true ? 'attribute_exists' : 'attribute_not_exists'; + subExpr = { + expressions: [`(${existsName}(${fieldName}))`], + expressionNames: createExpressionName(parent), + expressionValues: [], + }; + break; + } + case 'contains': + case 'notContains': + case 'beginsWith': { + const functionName = FUNCTION_MAP[name]; + subExpr = { + expressions: [`(${functionName}(${fieldName}, ${filedValueName}))`], + expressionNames: createExpressionName(parent), + expressionValues: createExpressionValue(parent, name, value, prefix), + }; + break; + } + case 'in': { + const operatorName = OPERATOR_MAP[name]; + subExpr = { + expressions: [`(${operatorName}(${filedValueName}, ${fieldName}))`], + expressionNames: createExpressionName(parent), + expressionValues: createExpressionValue(parent, name, value, prefix), + }; + break; + } + default: + subExpr = scopeExpression(generateFilterExpression(value, prefix, name)); + } + return merge(sum, subExpr); + }, + { + expressions: [], + expressionNames: {}, + expressionValues: {}, + }, + ); + + return expr; } function merge(expr1, expr2, joinCondition = 'AND') { - if (!expr2.expressions.length) { - return expr1; - } - - const res = { - expressions: [...expr1.expressions, expr1.expressions.length ? joinCondition : '', ...expr2.expressions], - expressionNames: { ...expr1.expressionNames, ...expr2.expressionNames }, - expressionValues: { ...expr1.expressionValues, ...expr2.expressionValues }, - }; - return res; + if (!expr2.expressions.length) { + return expr1; + } + + const res = { + expressions: [...expr1.expressions, expr1.expressions.length ? joinCondition : '', ...expr2.expressions], + expressionNames: { ...expr1.expressionNames, ...expr2.expressionNames }, + expressionValues: { ...expr1.expressionValues, ...expr2.expressionValues }, + }; + return res; } function createExpressionValueName(fieldName, op, prefix) { - return `:${[prefix, fieldName, op].filter((name) => name).join('_')}`; + return `:${[prefix, fieldName, op].filter((name) => name).join('_')}`; } function createExpressionName(fieldName) { - return { - [createExpressionFieldName(fieldName)]: fieldName, - }; + return { + [createExpressionFieldName(fieldName)]: fieldName, + }; } function createExpressionFieldName(fieldName) { - return `#${fieldName}`; + return `#${fieldName}`; } function createExpressionValue(fieldName, op, value, prefix) { - const exprName = createExpressionValueName(fieldName, op, prefix); - const exprValue = dynamodbUtils.toDynamoDB(value); - return { - [`${exprName}`]: exprValue, - }; + const exprName = createExpressionValueName(fieldName, op, prefix); + const exprValue = dynamodbUtils.toDynamoDB(value); + return { + [`${exprName}`]: exprValue, + }; } function scopeExpression(expr) { - const result = { ...expr }; - result.expressions = result.expressions.filter((e) => !!e); - if (result.expressions.length > 1) { - result.expressions = ['(' + result.expressions.join(' ') + ')']; - } - return result; + const result = { ...expr }; + result.expressions = result.expressions.filter((e) => !!e); + if (result.expressions.length > 1) { + result.expressions = ['(' + result.expressions.join(' ') + ')']; + } + return result; } diff --git a/rds/index.js b/rds/index.js index 9f70b5f..0e27e07 100644 --- a/rds/index.js +++ b/rds/index.js @@ -1,229 +1,229 @@ export function toJsonObject(inputStr) { - const input = JSON.parse(inputStr); - let perStatement = []; - for (const { records, columnMetadata } of input.sqlStatementResults) { - const statement = []; - - for (const record of records) { - const row = {}; - if (record.length !== columnMetadata.length) { - // TODO: what to do here?! - throw new Error("TODO"); - } + const input = JSON.parse(inputStr); + let perStatement = []; + for (const { records, columnMetadata } of input.sqlStatementResults) { + const statement = []; - for (const colNo in record) { + for (const record of records) { + const row = {}; + if (record.length !== columnMetadata.length) { + // TODO: what to do here?! + throw new Error("TODO"); + } - // TODO: what if the column is not a string? - const { stringValue } = record[colNo]; - const { label } = columnMetadata[colNo]; + for (const colNo in record) { - row[label] = stringValue; - } + // TODO: what if the column is not a string? + const { stringValue } = record[colNo]; + const { label } = columnMetadata[colNo]; - statement.push(row); - } + row[label] = stringValue; + } - perStatement.push(statement); + statement.push(row); } - return perStatement; + + perStatement.push(statement); + } + return perStatement; } export function select(s) { - return { type: "SELECT", properties: s }; + return { type: "SELECT", properties: s }; } export function remove(s) { - return { type: "REMOVE", properties: s }; + return { type: "REMOVE", properties: s }; } class PgStatementBuilder { - constructor() { - this.result = { - statements: [], - variableMap: {}, - variableTypeHintMap: {}, - }; - - this.variableIndex = 0; - } - - render(statements) { - for (const { type, properties } of statements) { - switch (type) { - case "SELECT": { - const { table, columns, where, orderBy, limit, offset } = properties; - const tableName = `"${table}"`; - let query; - - if (columns) { - const columnNames = columns.map(name => `"${name}"`).join(', '); - query = `SELECT ${columnNames} FROM ${tableName}`; - } else { - query = `SELECT * FROM ${tableName}`; - } - - if (where) { - const parts = this.buildWhereClause(where); - query = `${query} WHERE ${parts}`; - } - - - if (orderBy) { - let orderByParts = []; - for (let { column, dir } of orderBy) { - dir = dir || "ASC"; - orderByParts.push(`"${column}" ${dir}`); - } - - query = `${query} ORDER BY ${orderByParts.join(', ')}`; - - }; - - if (limit) { - const limitValue = this.newVariable(limit); - query = `${query} LIMIT ${limitValue}`; - } - - if (offset) { - const offsetValue = this.newVariable(offset); - query = `${query} OFFSET ${offsetValue}`; - } - - this.result.statements.push(query); - break; - } - case "REMOVE": { - const { table, where, returning, } = properties; - const tableName = `"${table}"`; - - let query = `DELETE FROM ${tableName}`; - - if (where) { - const parts = this.buildWhereClause(where); - query = `${query} WHERE ${parts}`; - } - - if (returning) { - const columnNames = returning.map(name => `"${name}"`).join(', '); - query = `${query} RETURNING ${columnNames}`; - } - - this.result.statements.push(query); - break; - } - default: - throw new Error(`TODO: "${type}" query unsupported`); + constructor() { + this.result = { + statements: [], + variableMap: {}, + variableTypeHintMap: {}, + }; + + this.variableIndex = 0; + } + + render(statements) { + for (const { type, properties } of statements) { + switch (type) { + case "SELECT": { + const { table, columns, where, orderBy, limit, offset } = properties; + const tableName = `"${table}"`; + let query; + + if (columns) { + const columnNames = columns.map(name => `"${name}"`).join(', '); + query = `SELECT ${columnNames} FROM ${tableName}`; + } else { + query = `SELECT * FROM ${tableName}`; + } + + if (where) { + const parts = this.buildWhereClause(where); + query = `${query} WHERE ${parts}`; + } + + + if (orderBy) { + let orderByParts = []; + for (let { column, dir } of orderBy) { + dir = dir || "ASC"; + orderByParts.push(`"${column}" ${dir}`); } + + query = `${query} ORDER BY ${orderByParts.join(', ')}`; + + }; + + if (limit) { + const limitValue = this.newVariable(limit); + query = `${query} LIMIT ${limitValue}`; + } + + if (offset) { + const offsetValue = this.newVariable(offset); + query = `${query} OFFSET ${offsetValue}`; + } + + this.result.statements.push(query); + break; } + case "REMOVE": { + const { table, where, returning, } = properties; + const tableName = `"${table}"`; - return this.result; - } + let query = `DELETE FROM ${tableName}`; + + if (where) { + const parts = this.buildWhereClause(where); + query = `${query} WHERE ${parts}`; + } - newVariable(value) { - const name = `:P${this.variableIndex}`; - if (value.type) { - this.result.variableMap[name] = value.value; - this.result.variableTypeHintMap[name] = value.type; - } else { - this.result.variableMap[name] = value; + if (returning) { + const columnNames = returning.map(name => `"${name}"`).join(', '); + query = `${query} RETURNING ${columnNames}`; + } + + this.result.statements.push(query); + break; } - this.variableIndex++; - return name; + default: + throw new Error(`TODO: "${type}" query unsupported`); + } } - buildWhereClause(where) { - let blocks = []; - if (where.or) { - const parts = []; - for (const part of where.or) { - parts.push(this.buildWhereStatement(part)); - } - blocks.push(parts.join(" OR ")); - } else if (where.and) { - const parts = []; - for (const part of where.and) { - parts.push(this.buildWhereStatement(part)); - } - blocks.push(parts.join(" AND ")); - } else { - // implicit single clause - blocks.push(this.buildWhereStatement(where, "", "")); - } + return this.result; + } - return blocks; + newVariable(value) { + const name = `:P${this.variableIndex}`; + if (value.type) { + this.result.variableMap[name] = value.value; + this.result.variableTypeHintMap[name] = value.type; + } else { + this.result.variableMap[name] = value; + } + this.variableIndex++; + return name; + } + + buildWhereClause(where) { + let blocks = []; + if (where.or) { + const parts = []; + for (const part of where.or) { + parts.push(this.buildWhereStatement(part)); + } + blocks.push(parts.join(" OR ")); + } else if (where.and) { + const parts = []; + for (const part of where.and) { + parts.push(this.buildWhereStatement(part)); + } + blocks.push(parts.join(" AND ")); + } else { + // implicit single clause + blocks.push(this.buildWhereStatement(where, "", "")); } - buildWhereStatement(defn, startGrouping = "(", endGrouping = ")") { - const columnName = Object.keys(defn)[0]; - const condition = defn[columnName]; - - const conditionType = Object.keys(condition)[0]; - const value = this.newVariable(condition[conditionType]); - switch (conditionType) { - case "eq": - return `${startGrouping}"${columnName}" = ${value}${endGrouping}`; - case "ne": - return `${startGrouping}"${columnName}" != ${value}${endGrouping}`; - case "gt": - return `${startGrouping}"${columnName}" > ${value}${endGrouping}`; - case "lt": - return `${startGrouping}"${columnName}" < ${value}${endGrouping}`; - case "ge": - return `${startGrouping}"${columnName}" >= ${value}${endGrouping}`; - case "le": - return `${startGrouping}"${columnName}" <= ${value}${endGrouping}`; - case "contains": - return `${startGrouping}"${columnName}" LIKE ${value}${endGrouping}`; - case "notContains": - return `${startGrouping}"${columnName}" NOT LIKE ${value}${endGrouping}`; - default: - throw new Error(`Unhandled condition type ${conditionType}`); - } + return blocks; + } + + buildWhereStatement(defn, startGrouping = "(", endGrouping = ")") { + const columnName = Object.keys(defn)[0]; + const condition = defn[columnName]; + + const conditionType = Object.keys(condition)[0]; + const value = this.newVariable(condition[conditionType]); + switch (conditionType) { + case "eq": + return `${startGrouping}"${columnName}" = ${value}${endGrouping}`; + case "ne": + return `${startGrouping}"${columnName}" != ${value}${endGrouping}`; + case "gt": + return `${startGrouping}"${columnName}" > ${value}${endGrouping}`; + case "lt": + return `${startGrouping}"${columnName}" < ${value}${endGrouping}`; + case "ge": + return `${startGrouping}"${columnName}" >= ${value}${endGrouping}`; + case "le": + return `${startGrouping}"${columnName}" <= ${value}${endGrouping}`; + case "contains": + return `${startGrouping}"${columnName}" LIKE ${value}${endGrouping}`; + case "notContains": + return `${startGrouping}"${columnName}" NOT LIKE ${value}${endGrouping}`; + default: + throw new Error(`Unhandled condition type ${conditionType}`); } + } } export function createPgStatement(...statements) { - let builder = new PgStatementBuilder(); - return builder.render(statements); + let builder = new PgStatementBuilder(); + return builder.render(statements); } export const typeHint = { - DECIMAL: function(value) { - return { - type: "DECIMAL", - value, - }; - }, - JSON: function(value) { - return { - type: "JSON", - value, - }; - }, - TIME: function(value) { - return { - type: "TIME", - value, - }; - }, - DATE: function(value) { - return { - type: "DATE", - value, - }; - }, - UUID: function(value) { - return { - type: "UUID", - value, - }; - }, - TIMESTAMP: function(value) { - return { - type: "TIMESTAMP", - value: value.toISOString(), - }; - } + DECIMAL: function(value) { + return { + type: "DECIMAL", + value, + }; + }, + JSON: function(value) { + return { + type: "JSON", + value, + }; + }, + TIME: function(value) { + return { + type: "TIME", + value, + }; + }, + DATE: function(value) { + return { + type: "DATE", + value, + }; + }, + UUID: function(value) { + return { + type: "UUID", + value, + }; + }, + TIMESTAMP: function(value) { + return { + type: "TIMESTAMP", + value: value.toISOString(), + }; + } }; diff --git a/scripts/testcode.js b/scripts/testcode.js index 8dada1f..ca7a508 100644 --- a/scripts/testcode.js +++ b/scripts/testcode.js @@ -3,8 +3,8 @@ import * as ddb from '@aws-appsync/utils/dynamodb'; export function request(ctx) { return util.transform.toDynamoDBFilterExpression({ - "title":{ - "contains":"Hello World" + "title": { + "contains": "Hello World" } }); }