From e243e0eaba05206022396e8527a520d4d069de09 Mon Sep 17 00:00:00 2001 From: Simon Walker Date: Wed, 31 Jan 2024 16:57:34 +0000 Subject: [PATCH] Support mysql --- .../__snapshots__/resolvers.test.js.snap | 49 ++++++- __tests__/resolvers.test.js | 130 ++++++++++++++---- rds/index.js | 43 +++--- 3 files changed, 179 insertions(+), 43 deletions(-) diff --git a/__tests__/__snapshots__/resolvers.test.js.snap b/__tests__/__snapshots__/resolvers.test.js.snap index 33d2189..af627d6 100644 --- a/__tests__/__snapshots__/resolvers.test.js.snap +++ b/__tests__/__snapshots__/resolvers.test.js.snap @@ -38,7 +38,50 @@ exports[`dynamodb resolvers something 2`] = ` ] `; -exports[`rds resolvers createPgStatement-remove 1`] = ` +exports[`rds resolvers mysql remove 1`] = ` +{ + "statements": [ + "DELETE FROM \`persons\` WHERE \`id\` = :P0", + ], + "variableMap": { + ":P0": "1232", + }, + "variableTypeHintMap": {}, +} +`; + +exports[`rds resolvers mysql select 1`] = ` +{ + "statements": [ + "SELECT \`id\`, \`name\` FROM \`UserGroup\` WHERE (\`name\` = :P0) OR (\`id\` > :P1) ORDER BY \`name\` ASC, \`id\` DESC LIMIT :P2 OFFSET :P3", + ], + "variableMap": { + ":P0": "Stephane", + ":P1": 10, + ":P2": 10, + ":P3": 1, + }, + "variableTypeHintMap": {}, +} +`; + +exports[`rds resolvers mysql type hints 1`] = ` +{ + "statements": [ + "SELECT * FROM \`UserGroup\` WHERE (\`id\` = :P0) AND (\`started\` < :P1)", + ], + "variableMap": { + ":P0": "1232", + ":P1": "2022-03-02T00:00:00.000Z", + }, + "variableTypeHintMap": { + ":P0": "UUID", + ":P1": "TIMESTAMP", + }, +} +`; + +exports[`rds resolvers postgresql remove 1`] = ` { "statements": [ "DELETE FROM "persons" WHERE "id" = :P0 RETURNING "id", "name"", @@ -50,7 +93,7 @@ exports[`rds resolvers createPgStatement-remove 1`] = ` } `; -exports[`rds resolvers createPgStatement-select 1`] = ` +exports[`rds resolvers postgresql select 1`] = ` { "statements": [ "SELECT "id", "name" FROM "UserGroup" WHERE ("name" = :P0) OR ("id" > :P1) ORDER BY "name" ASC, "id" DESC LIMIT :P2 OFFSET :P3", @@ -65,7 +108,7 @@ exports[`rds resolvers createPgStatement-select 1`] = ` } `; -exports[`rds resolvers createPgStatement-typeHint 1`] = ` +exports[`rds resolvers postgresql type hints 1`] = ` { "statements": [ "SELECT * FROM "UserGroup" WHERE ("id" = :P0) AND ("started" < :P1)", diff --git a/__tests__/resolvers.test.js b/__tests__/resolvers.test.js index 5f88304..3bf86f4 100644 --- a/__tests__/resolvers.test.js +++ b/__tests__/resolvers.test.js @@ -201,14 +201,15 @@ describe("rds resolvers", () => { await checkResolverValid(code, responseContext, "response"); }); - test("createPgStatement-typeHint", async () => { - const code = ` + describe("mysql", () => { + test("type hints", async () => { + const code = ` export function request(ctx) { const whereClause = { and:[ { id: { eq: rds.typeHint.UUID(ctx.args.id) } }, { started: { lt: rds.typeHint.TIMESTAMP(ctx.args.started) } } ] }; - return rds.createPgStatement(rds.select({ + return rds.createMySQLStatement(rds.select({ table: "UserGroup", where: whereClause, })); @@ -216,20 +217,100 @@ 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("select", async () => { + const code = ` + export function request(ctx) { + const whereClause = { or: [ + { name: { eq: 'Stephane'} }, + { id: { gt: 10 } } + ]} + return rds.createMySQLStatement(rds.select({ + table: "UserGroup", + where: whereClause, + limit: 10, + offset: 1, + columns: ['id', 'name'], + orderBy: [{column: 'name'}, {column: 'id', dir: 'DESC'}] + })); + } + + export function response(ctx) {} + `; + + const requestContext = {}; + + await checkResolverValid(code, requestContext, "request"); + + }); + test("remove", async () => { + const code = ` + export function request(ctx) { + const id = ctx.args.id; + const where = { id: { eq: id } }; + const deleteStatement = rds.remove({ + table: 'persons', + where: where, + }); + + return rds.createMySQLStatement(deleteStatement); + } + export function response(ctx) {} + `; + + const requestContext = { + arguments: { + id: "1232" + } + }; + + await checkResolverValid(code, requestContext, "request"); + + }); }); - test("createPgStatement-select", async () => { - const code = ` + describe("postgresql", () => { + test("type hints", async () => { + const code = ` + export function request(ctx) { + const whereClause = { and:[ + { id: { eq: rds.typeHint.UUID(ctx.args.id) } }, + { started: { lt: rds.typeHint.TIMESTAMP(ctx.args.started) } } + ] }; + return rds.createPgStatement(rds.select({ + table: "UserGroup", + where: whereClause, + })); + } + + export function response(ctx) {} + ` + const requestContext = { + arguments: { + id: "1232", + name: "hello", + started: new Date(2022, 2, 2), + } + }; + + await checkResolverValid(code, requestContext, "request"); + + }); + + test("select", async () => { + const code = ` export function request(ctx) { const whereClause = { or: [ { name: { eq: 'Stephane'} }, @@ -248,14 +329,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("remove", async () => { + const code = ` export function request(ctx) { const id = ctx.args.id; const where = { id: { eq: id } }; @@ -270,14 +351,15 @@ 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/rds/index.js b/rds/index.js index 0e27e07..5b118cc 100644 --- a/rds/index.js +++ b/rds/index.js @@ -38,8 +38,9 @@ export function remove(s) { return { type: "REMOVE", properties: s }; } -class PgStatementBuilder { - constructor() { +class StatementBuilder { + constructor({ quoteChar }) { + this.quoteChar = quoteChar; this.result = { statements: [], variableMap: {}, @@ -54,11 +55,11 @@ class PgStatementBuilder { switch (type) { case "SELECT": { const { table, columns, where, orderBy, limit, offset } = properties; - const tableName = `"${table}"`; + const tableName = `${this.quoteChar}${table}${this.quoteChar}`; let query; if (columns) { - const columnNames = columns.map(name => `"${name}"`).join(', '); + const columnNames = columns.map(name => `${this.quoteChar}${name}${this.quoteChar}`).join(', '); query = `SELECT ${columnNames} FROM ${tableName}`; } else { query = `SELECT * FROM ${tableName}`; @@ -74,7 +75,7 @@ class PgStatementBuilder { let orderByParts = []; for (let { column, dir } of orderBy) { dir = dir || "ASC"; - orderByParts.push(`"${column}" ${dir}`); + orderByParts.push(`${this.quoteChar}${column}${this.quoteChar} ${dir}`); } query = `${query} ORDER BY ${orderByParts.join(', ')}`; @@ -96,7 +97,7 @@ class PgStatementBuilder { } case "REMOVE": { const { table, where, returning, } = properties; - const tableName = `"${table}"`; + const tableName = `${this.quoteChar}${table}${this.quoteChar}`; let query = `DELETE FROM ${tableName}`; @@ -106,7 +107,7 @@ class PgStatementBuilder { } if (returning) { - const columnNames = returning.map(name => `"${name}"`).join(', '); + const columnNames = returning.map(name => `${this.quoteChar}${name}${this.quoteChar}`).join(', '); query = `${query} RETURNING ${columnNames}`; } @@ -163,21 +164,21 @@ class PgStatementBuilder { const value = this.newVariable(condition[conditionType]); switch (conditionType) { case "eq": - return `${startGrouping}"${columnName}" = ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} = ${value}${endGrouping}`; case "ne": - return `${startGrouping}"${columnName}" != ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} != ${value}${endGrouping}`; case "gt": - return `${startGrouping}"${columnName}" > ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} > ${value}${endGrouping}`; case "lt": - return `${startGrouping}"${columnName}" < ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} < ${value}${endGrouping}`; case "ge": - return `${startGrouping}"${columnName}" >= ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} >= ${value}${endGrouping}`; case "le": - return `${startGrouping}"${columnName}" <= ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} <= ${value}${endGrouping}`; case "contains": - return `${startGrouping}"${columnName}" LIKE ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} LIKE ${value}${endGrouping}`; case "notContains": - return `${startGrouping}"${columnName}" NOT LIKE ${value}${endGrouping}`; + return `${startGrouping}${this.quoteChar}${columnName}${this.quoteChar} NOT LIKE ${value}${endGrouping}`; default: throw new Error(`Unhandled condition type ${conditionType}`); } @@ -185,10 +186,20 @@ class PgStatementBuilder { } export function createPgStatement(...statements) { - let builder = new PgStatementBuilder(); + let builder = new StatementBuilder({ + quoteChar: '"', + }); return builder.render(statements); } +export function createMySQLStatement(...statements) { + let builder = new StatementBuilder({ + quoteChar: '`', + }); + return builder.render(statements); +} + + export const typeHint = { DECIMAL: function(value) { return {