diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c4245ce2..a69eb12e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,6 +7,17 @@ jobs: runs-on: ${{ matrix.operating-system }} + services: + postgres: + image: postgres:12.0 + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: 12345678 + POSTGRES_DB: relay + ports: + - 5432:5432 + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 + strategy: fail-fast: false matrix: @@ -21,17 +32,6 @@ jobs: cache: 'yarn' node-version: ${{ matrix.node-version }} - - name: Setup MySQL 8.0 - uses: ankane/setup-mysql@v1 - with: - mysql-version: 8.0 - database: relay - - - name: Config MySQL - run: | - mysqladmin -u root password '12345678' - exit; - - name: Install dependencies run: yarn install --pure-lockfile - name: Check formatting of Contract diff --git a/packages/relay/config/config.yaml b/packages/relay/config/config.yaml index a2248c74..5904f6f0 100644 --- a/packages/relay/config/config.yaml +++ b/packages/relay/config/config.yaml @@ -13,12 +13,11 @@ server: database: host: "${DATABASE_HOST}" user: "${DATABASE_USER}" - database: "${DATABASE_NAME}" password: "${DATABASE_PASSWORD}" + database: "${DATABASE_NAME}" port: "${DATABASE_PORT}" - waitForConnections: true - connectionLimit: 30 - queueLimit: 0 + connectionTimeoutMillis: 2000 + max: 100 ################################################################################ ## Logging options ## diff --git a/packages/relay/config/config_test.yaml b/packages/relay/config/config_test.yaml index a2248c74..5904f6f0 100644 --- a/packages/relay/config/config_test.yaml +++ b/packages/relay/config/config_test.yaml @@ -13,12 +13,11 @@ server: database: host: "${DATABASE_HOST}" user: "${DATABASE_USER}" - database: "${DATABASE_NAME}" password: "${DATABASE_PASSWORD}" + database: "${DATABASE_NAME}" port: "${DATABASE_PORT}" - waitForConnections: true - connectionLimit: 30 - queueLimit: 0 + connectionTimeoutMillis: 2000 + max: 100 ################################################################################ ## Logging options ## diff --git a/packages/relay/docker-compose.yml b/packages/relay/docker-compose.yml index 33a53ed9..08583ead 100644 --- a/packages/relay/docker-compose.yml +++ b/packages/relay/docker-compose.yml @@ -1,18 +1,25 @@ version: '3.3' - services: - mysql: - image: "mysql:8.0" - cap_add: - - SYS_NICE + postgres: + image: postgres:12.0 + container_name: postgres ports: - - "3306:3306" - command: --default-authentication-plugin=mysql_native_password + - '5432:5432' restart: always - volumes: - - mysql_db:/var/lib/mysql + command: + [ + "postgres", + "-c", "shared_preload_libraries=pg_stat_statements", + "-c", "max_connections=1000" + ] environment: - MYSQL_ROOT_PASSWORD: "12345678" + POSTGRES_PASSWORD: 12345678 + POSTGRES_USER: root + POSTGRES_DB: relay + PGDATA: /postgresql/data + POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C" + volumes: + - postgres_db:/postgresql/data volumes: - mysql_db: + postgres_db: diff --git a/packages/relay/env/.env.sample b/packages/relay/env/.env.sample index 11cec4f2..0efcaf4e 100644 --- a/packages/relay/env/.env.sample +++ b/packages/relay/env/.env.sample @@ -13,7 +13,7 @@ DATABASE_HOST=127.0.0.1 DATABASE_USER=root DATABASE_NAME=relay DATABASE_PASSWORD=12345678 -DATABASE_PORT=3306 +DATABASE_PORT=5432 # 0xDc245797409fb79446523Fa1A4ca97294eef22EE DEPLOYER=0x2b5d5cc406b66c0398d0b8327d340cb4f6e30540621802e115506fe001398ba3 diff --git a/packages/relay/package.json b/packages/relay/package.json index b4d1adda..efc77342 100644 --- a/packages/relay/package.json +++ b/packages/relay/package.json @@ -54,6 +54,7 @@ "@types/urijs": "^1.19.12" }, "dependencies": { + "@types/pg": "^8.10.7", "argparse": "^2.0.1", "assert": "^2.0.0", "axios": "^0.26.0", @@ -74,7 +75,7 @@ "ip": "^1.1.5", "moment": "^2.29.1", "mybatis-mapper": "^0.7.1", - "mysql2": "^3.6.3", + "pg": "^8.11.3", "prettier": "^2.5.1", "prettier-plugin-solidity": "^1.1.1", "smart-buffer": "^4.1.0", diff --git a/packages/relay/src/common/Config.ts b/packages/relay/src/common/Config.ts index 099cb937..fc628415 100644 --- a/packages/relay/src/common/Config.ts +++ b/packages/relay/src/common/Config.ts @@ -187,48 +187,29 @@ export class DatabaseConfig implements IDatabaseConfig { port: number; /** - * multiple Statements exec config + * number of milliseconds to wait before timing out when connecting a new client + * by default this is 0 which means no timeout */ - multipleStatements: boolean; + connectionTimeoutMillis: number; /** - * Determines the pool's action when no connections are available - * and the limit has been reached. - * If true, the pool will queue the connection request and call - * it when one becomes available. - * If false, the pool will immediately call back with an error. + * maximum number of clients the pool should contain + * by default this is set to 10. */ - waitForConnections: boolean; - - /** - * The maximum number of connections to create at once. - */ - connectionLimit: number; - - /** - * The maximum number of connection requests the pool - * will queue before returning an error from getConnection. - * If set to 0, there is no limit to the number of queued connection requests. - */ - queueLimit: number; + max: number; /** * Constructor - * @param host Mysql database host - * @param user Mysql database user - * @param password Mysql database password - * @param database Mysql database name - * @param port Mysql database port - * @param multipleStatements Mysql allow multiple statement to execute (true / false) - * @param waitForConnections Determines the pool's action when no connections are available - * and the limit has been reached. - * If true, the pool will queue the connection request and call - * it when one becomes available. - * If false, the pool will immediately call back with an error. - * @param connectionLimit The maximum number of connections to create at once. - * @param queueLimit The maximum number of connection requests the pool - * will queue before returning an error from getConnection. - * If set to 0, there is no limit to the number of queued connection requests. + * @param host Postgresql database host + * @param user Postgresql database user + * @param password Postgresql database password + * @param database Postgresql database name + * @param port Postgresql database port + * @param connectionTimeoutMillis Number of milliseconds to wait before + * timing out when connecting a new client. + * By default this is 0 which means no timeout. + * @param max Number of milliseconds to wait before timing out when + * connecting a new client by default this is 0 which means no timeout. */ constructor( host?: string, @@ -236,10 +217,8 @@ export class DatabaseConfig implements IDatabaseConfig { password?: string, database?: string, port?: number, - multipleStatements?: boolean, - waitForConnections?: boolean, - connectionLimit?: number, - queueLimit?: number + connectionTimeoutMillis?: number, + max?: number ) { const conf = extend(true, {}, DatabaseConfig.defaultValue()); extend(true, conf, { @@ -248,20 +227,16 @@ export class DatabaseConfig implements IDatabaseConfig { password, database, port, - multipleStatements, - waitForConnections, - connectionLimit, - queueLimit, + connectionTimeoutMillis, + max, }); this.host = conf.host; this.user = conf.user; this.password = conf.password; this.database = conf.database; this.port = conf.port; - this.multipleStatements = conf.multipleStatements; - this.waitForConnections = conf.waitForConnections; - this.connectionLimit = conf.connectionLimit; - this.queueLimit = conf.queueLimit; + this.connectionTimeoutMillis = conf.connectionTimeoutMillis; + this.max = conf.max; } /** @@ -272,12 +247,10 @@ export class DatabaseConfig implements IDatabaseConfig { host: "localhost", user: "root", password: "12345678", - database: "boascan", - port: 3306, - multipleStatements: true, - waitForConnections: true, - connectionLimit: 10, - queueLimit: 0, + database: "relay", + port: 5432, + connectionTimeoutMillis: 2000, + max: 20, }; } @@ -293,10 +266,8 @@ export class DatabaseConfig implements IDatabaseConfig { this.password = conf.password; this.database = conf.database; this.port = conf.port; - this.multipleStatements = conf.multipleStatements; - this.waitForConnections = conf.waitForConnections; - this.connectionLimit = conf.connectionLimit; - this.queueLimit = conf.queueLimit; + this.connectionTimeoutMillis = conf.connectionTimeoutMillis; + this.max = conf.max; } } @@ -494,30 +465,16 @@ export interface IDatabaseConfig { port: number; /** - * Multiple Statements execution statement Option - */ - multipleStatements: boolean; - - /** - * Determines the pool's action when no connections are available - * and the limit has been reached. - * If true, the pool will queue the connection request and call - * it when one becomes available. - * If false, the pool will immediately call back with an error. - */ - waitForConnections: boolean; - - /** - * The maximum number of connections to create at once. + * number of milliseconds to wait before timing out when connecting a new client + * by default this is 0 which means no timeout */ - connectionLimit: number; + connectionTimeoutMillis: number; /** - * The maximum number of connection requests the pool - * will queue before returning an error from getConnection. - * If set to 0, there is no limit to the number of queued connection requests. + * maximum number of clients the pool should contain + * by default this is set to 10. */ - queueLimit: number; + max: number; } /** diff --git a/packages/relay/src/main.ts b/packages/relay/src/main.ts index 3de6d78d..cf09d19e 100644 --- a/packages/relay/src/main.ts +++ b/packages/relay/src/main.ts @@ -2,7 +2,7 @@ import { Config } from "./common/Config"; import { logger, Logger } from "./common/Logger"; import { DefaultServer } from "./DefaultServer"; import { RelayStorage } from "./storage/RelayStorage"; -import { Storage } from "./storage/Storage"; +import { ContractUtils } from "./utils/ContractUtils"; let server: DefaultServer; @@ -34,27 +34,24 @@ async function main() { logger.info(`address: ${config.server.address}`); logger.info(`port: ${config.server.port}`); - Storage.waiteForConnection(config.database) - .then(() => { - return RelayStorage.make(config.database); - }) - .then(async (storage) => { - server = new DefaultServer(config, storage); - return server.start().catch((error: any) => { - // handle specific listen errors with friendly messages - switch (error.code) { - case "EACCES": - logger.error(`${config.server.port} requires elevated privileges`); - break; - case "EADDRINUSE": - logger.error(`Port ${config.server.port} is already in use`); - break; - default: - logger.error(`An error occurred while starting the server: ${error.stack}`); - } - process.exit(1); - }); - }); + await ContractUtils.delay(3000); + const storage = await RelayStorage.make(config.database); + + server = new DefaultServer(config, storage); + return server.start().catch((error: any) => { + // handle specific listen errors with friendly messages + switch (error.code) { + case "EACCES": + logger.error(`${config.server.port} requires elevated privileges`); + break; + case "EADDRINUSE": + logger.error(`Port ${config.server.port} is already in use`); + break; + default: + logger.error(`An error occurred while starting the server: ${error.stack}`); + } + process.exit(1); + }); } // We recommend this pattern to be able to use async/await everywhere diff --git a/packages/relay/src/storage/RelayStorage.ts b/packages/relay/src/storage/RelayStorage.ts index 7e8635c9..2914a543 100644 --- a/packages/relay/src/storage/RelayStorage.ts +++ b/packages/relay/src/storage/RelayStorage.ts @@ -4,8 +4,7 @@ import { Storage } from "./Storage"; import { BigNumber } from "ethers"; import MybatisMapper from "mybatis-mapper"; -// tslint:disable-next-line:no-submodule-imports -import * as mysql from "mysql2/promise"; + import path from "path"; import { LoyaltyPaymentInputData, LoyaltyPaymentInputDataStatus, LoyaltyType } from "../types"; @@ -17,13 +16,15 @@ export class RelayStorage extends Storage { super(databaseConfig, callback); MybatisMapper.createMapper([path.resolve(Utils.getInitCWD(), "src/storage/mapper/table.xml")]); MybatisMapper.createMapper([path.resolve(Utils.getInitCWD(), "src/storage/mapper/payment.xml")]); + this.createTables() + .then(() => { + if (callback != null) callback(null); + }) + .catch((err: any) => { + if (callback != null) callback(err); + }); } - /** - * Construct an instance of `Storage` using `Promise` API. - * - * @param databaseConfig - */ public static make(databaseConfig: IDatabaseConfig): Promise { return new Promise((resolve, reject) => { const result = new RelayStorage(databaseConfig, (err: Error | null) => { @@ -34,23 +35,15 @@ export class RelayStorage extends Storage { }); } - /** - * Creates tables related to the ledger. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ - public createTables(): Promise { - return this.exec(MybatisMapper.getStatement("table", "create_table")); + public createTables(): Promise { + return this.queryForMapper("table", "create_table", {}); } - /** - * Drop Database - * Use this only in the test code. - * @param database The name of database - */ - public async dropTestDB(database: any): Promise { - return this.exec(MybatisMapper.getStatement("table", "drop_table", { database })); + public async dropTestDB(database: any): Promise { + await this.exec( + `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '${database}'` + ); + await this.queryForMapper("table", "drop_table", { database }); } public postPayment( @@ -64,28 +57,22 @@ export class RelayStorage extends Storage { purchaseAmount: BigNumber, feeAmount: BigNumber, totalAmount: BigNumber, - paymentStatus: LoyaltyPaymentInputDataStatus, - conn?: mysql.PoolConnection + paymentStatus: LoyaltyPaymentInputDataStatus ): Promise { return new Promise(async (resolve, reject) => { - this.queryForMapper( - "payment", - "postPayment", - { - paymentId, - purchaseId, - amount: amount.toString(), - currency, - shopId, - account, - loyaltyType, - purchaseAmount: purchaseAmount.toString(), - feeAmount: feeAmount.toString(), - totalAmount: totalAmount.toString(), - paymentStatus, - }, - conn - ) + this.queryForMapper("payment", "postPayment", { + paymentId, + purchaseId, + amount: amount.toString(), + currency, + shopId, + account, + loyaltyType, + purchaseAmount: purchaseAmount.toString(), + feeAmount: feeAmount.toString(), + totalAmount: totalAmount.toString(), + paymentStatus, + }) .then(() => { return resolve(); }) @@ -96,23 +83,23 @@ export class RelayStorage extends Storage { }); } - public getPayment(paymentId: string, conn?: mysql.PoolConnection): Promise { + public getPayment(paymentId: string): Promise { return new Promise(async (resolve, reject) => { - this.queryForMapper("payment", "getPayment", { paymentId }, conn) - .then((rows: any[]) => { - if (rows.length > 0) { - const m = rows[0]; + this.queryForMapper("payment", "getPayment", { paymentId }) + .then((result) => { + if (result.rows.length > 0) { + const m = result.rows[0]; return resolve({ paymentId: m.paymentId, purchaseId: m.purchaseId, - amount: m.amount, + amount: BigNumber.from(m.amount), currency: m.currency, shopId: m.shopId, account: m.account, loyaltyType: m.loyaltyType, - purchaseAmount: m.purchaseAmount, - feeAmount: m.feeAmount, - totalAmount: m.totalAmount, + purchaseAmount: BigNumber.from(m.purchaseAmount), + feeAmount: BigNumber.from(m.feeAmount), + totalAmount: BigNumber.from(m.totalAmount), paymentStatus: m.paymentStatus, }); } else { @@ -126,19 +113,19 @@ export class RelayStorage extends Storage { }); } - public updatePaymentStatus( - paymentId: string, - paymentStatus: LoyaltyPaymentInputDataStatus, - conn?: mysql.PoolConnection - ): Promise { - return this.queryForMapper( - "payment", - "updateStatus", - { + public updatePaymentStatus(paymentId: string, paymentStatus: LoyaltyPaymentInputDataStatus): Promise { + return new Promise(async (resolve, reject) => { + this.queryForMapper("payment", "updateStatus", { paymentId, paymentStatus, - }, - conn - ); + }) + .then(() => { + return resolve(); + }) + .catch((reason) => { + if (reason instanceof Error) return reject(reason); + return reject(new Error(reason)); + }); + }); } } diff --git a/packages/relay/src/storage/Storage.ts b/packages/relay/src/storage/Storage.ts index ea303761..8016fdca 100644 --- a/packages/relay/src/storage/Storage.ts +++ b/packages/relay/src/storage/Storage.ts @@ -1,235 +1,56 @@ import MybatisMapper, { Params } from "mybatis-mapper"; -// tslint:disable-next-line:no-submodule-imports -import * as mysql from "mysql2/promise"; +import pg, { QueryResult, QueryResultRow } from "pg"; import { IDatabaseConfig } from "../common/Config"; -import { logger } from "../common/Logger"; export class Storage { - /** - * The instance of mysql Connection Pool. - */ - protected pool: mysql.Pool; + protected pool: pg.Pool; - /** - * Constructor - * @param databaseConfig Valid value is of type IDatabaseConfig, - * @param callback If provided, this function will be called when - * the database was opened successfully or when an error occurred. - * The first argument is an error object. If there is no error, this value is null. - */ constructor(databaseConfig: IDatabaseConfig, callback: (err: Error | null) => void) { - const dbconfig: IDatabaseConfig = { - host: databaseConfig.host, + this.pool = new pg.Pool({ user: databaseConfig.user, password: databaseConfig.password, - multipleStatements: databaseConfig.multipleStatements, - port: Number(databaseConfig.port), - waitForConnections: databaseConfig.waitForConnections, - connectionLimit: Number(databaseConfig.connectionLimit), - queueLimit: Number(databaseConfig.queueLimit), - }; - this.pool = mysql.createPool(dbconfig); - - this.query(`CREATE DATABASE IF NOT EXISTS \`${databaseConfig.database}\`;`, []) - .then(async (result) => { - dbconfig.database = databaseConfig.database; - this.pool = mysql.createPool(dbconfig); - this.createTables() - .then(() => { - if (callback != null) callback(null); - }) - .catch((err: any) => { - if (callback != null) callback(err); - }); - }) - .catch((err) => { - if (callback != null) callback(err); - }); - } - - /** - * The main thread waits until the database is accessed. - * The maximum waiting time is about 50 seconds. - * @param databaseConfig Valid value is of type IDatabaseConfig, - */ - public static waiteForConnection(databaseConfig: IDatabaseConfig): Promise { - const connection_config: mysql.ConnectionOptions = { host: databaseConfig.host, - user: databaseConfig.user, - password: databaseConfig.password, - multipleStatements: databaseConfig.multipleStatements, - port: Number(databaseConfig.port), - }; - - return new Promise((resolve) => { - let try_count = 0; - const check_connection = async () => { - try_count++; - const connection = await mysql.createConnection(connection_config); - connection - .connect() - .then(() => { - return resolve(); - }) - .catch((err) => { - if (try_count < 10) { - setTimeout(() => { - check_connection(); - }, 5000); - } - }); - }; - check_connection(); + port: databaseConfig.port, + database: databaseConfig.database, + max: databaseConfig.max, + connectionTimeoutMillis: databaseConfig.connectionTimeoutMillis, }); } - /** - * Creates tables. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ public createTables(): Promise { return new Promise((resolve, reject) => { resolve(); }); } - /** - * Returns the DB connection of the Connection Pool. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ - - public getConnection(): Promise { - return this.pool.getConnection(); - } - - /** - * Close the database - */ public close(): Promise { return this.pool.end(); } - /** - * Execute SQL to query the database for data. - * @param sql The SQL query to run. - * @param params When the SQL statement contains placeholders, - * you can pass them in here. - * @param conn Use this if it is providing a db connection. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called with the records - * and if an error occurs the `.catch` is called with an error. - */ - public query(sql: string, params: any, conn?: mysql.PoolConnection): Promise { - return new Promise(async (resolve, reject) => { - let connection: mysql.PoolConnection | null = null; - try { - if (!conn) connection = await this.getConnection(); - else connection = conn; - - const [rows] = await connection.query(sql, params); - if (!conn) connection.release(); - resolve(rows as any); - } catch (err) { - if (!conn && connection) connection.release(); - return reject(err); - } - }); - } - - /** - * Execute SQL to query the database for data with MybatisMapper. - * @param namespace namespace of the query defined in the mapper file - * @param sql_id ID of the query defined in the mapper file - * @param param When the SQL statement contains placeholders, - * you can pass them in here. - * @param conn Use this if it is providing a db connection. - */ - public queryForMapper( + public queryForMapper( namespace: string, sql_id: string, - param?: Params, - conn?: mysql.PoolConnection - ): Promise { - return new Promise(async (resolve, reject) => { - let connection: mysql.PoolConnection | null = null; + param?: Params + ): Promise> { + return new Promise>(async (resolve, reject) => { try { - if (!conn) connection = await this.getConnection(); - else connection = conn; const sql = MybatisMapper.getStatement(namespace, sql_id, param, { language: "sql", indent: " " }); - const [rows] = await connection.query(sql); - if (!conn) connection.release(); - resolve(rows as any); + const res = await this.pool.query(sql); + resolve(res); } catch (err) { - if (!conn && connection) connection.release(); return reject(err); } }); } - /** - * Executes the SQL query - * @param sql The SQL query to run. - * @param conn Use this if it is providing a db connection. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ - protected exec(sql: string, conn?: mysql.PoolConnection): Promise { + protected exec(sql: string): Promise { return new Promise(async (resolve, reject) => { - let connection: mysql.PoolConnection | null = null; try { - if (!conn) connection = await this.getConnection(); - else connection = conn; - await connection.query(sql); - if (!conn) connection.release(); + await this.pool.query(sql); resolve(); } catch (err) { - if (!conn && connection) connection.release(); return reject(err); } }); } - - /** - * Mysql transaction statement - * To start a transaction explicitly, - * Open a transaction by issuing the beginning function - * the transaction is open until it is explicitly - * committed or rolled back. - * @param conn Use this if it is providing a db connection must be released after transaction end. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ - public begin(conn: mysql.PoolConnection): Promise { - return conn.beginTransaction(); - } - - /** - * Mysql transaction statement - * Commit the changes to the database by using this. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ - public commit(conn: mysql.PoolConnection): Promise { - return conn.commit(); - } - - /** - * Mysql transaction statement - * If it does not want to save the changes, - * it can roll back using this. - * @param conn Use this if it is providing a db connection must be released after transaction end. - * @returns Returns the Promise. If it is finished successfully the `.then` - * of the returned Promise is called and if an error occurs the `.catch` - * is called with an error. - */ - public rollback(conn: mysql.PoolConnection): Promise { - return conn.rollback(); - } } diff --git a/packages/relay/src/storage/mapper/payment.xml b/packages/relay/src/storage/mapper/payment.xml index 73170b2d..9709dad3 100644 --- a/packages/relay/src/storage/mapper/payment.xml +++ b/packages/relay/src/storage/mapper/payment.xml @@ -5,17 +5,17 @@ INSERT INTO payments ( - paymentId , - purchaseId , - amount , - currency , - shopId , - account , - loyaltyType , - purchaseAmount , - feeAmount , - totalAmount , - paymentStatus + "paymentId" , + "purchaseId" , + "amount" , + "currency" , + "shopId" , + "account" , + "loyaltyType" , + "purchaseAmount" , + "feeAmount" , + "totalAmount" , + "paymentStatus" ) VALUES ( @@ -31,27 +31,17 @@ #{totalAmount}, ${paymentStatus} ) - ON DUPLICATE KEY UPDATE - purchaseId = VALUES(purchaseId), - amount = VALUES(amount), - currency = VALUES(currency), - shopId = VALUES(shopId), - account = VALUES(account), - loyaltyType = VALUES(loyaltyType), - purchaseAmount = VALUES(purchaseAmount), - feeAmount = VALUES(feeAmount), - totalAmount = VALUES(totalAmount), - paymentStatus = VALUES(paymentStatus); + ON CONFLICT ("paymentId") DO NOTHING; UPDATE payments SET - paymentStatus = #{paymentStatus} - WHERE paymentId = #{paymentId} + "paymentStatus" = #{paymentStatus} + WHERE "paymentId" = #{paymentId} diff --git a/packages/relay/src/storage/mapper/table.xml b/packages/relay/src/storage/mapper/table.xml index 65063eae..0c6dd658 100644 --- a/packages/relay/src/storage/mapper/table.xml +++ b/packages/relay/src/storage/mapper/table.xml @@ -1,34 +1,22 @@ - - paymentId: string; - purchaseId: string; - amount: BigNumber; - currency: string; - shopId: string; - account: string; - loyaltyType: LoyaltyType; - purchaseAmount: BigNumber; - feeAmount: BigNumber; - totalAmount: BigNumber; - paymentStatus: LoyaltyPaymentInputDataStatus; CREATE TABLE IF NOT EXISTS payments ( - paymentId VARCHAR(66) NOT NULL, - purchaseId VARCHAR(66) NOT NULL, - amount VARCHAR(32) NOT NULL, - currency VARCHAR(12) NOT NULL, - shopId VARCHAR(66) NOT NULL, - account VARCHAR(42) NOT NULL, - loyaltyType INTEGER, - purchaseAmount VARCHAR(32) NOT NULL, - feeAmount VARCHAR(32) NOT NULL, - totalAmount VARCHAR(32) NOT NULL, - paymentStatus INTEGER, - PRIMARY KEY (paymentId) - ) ENGINE = InnoDB DEFAULT CHARSET = utf8; + "paymentId" VARCHAR(66) NOT NULL, + "purchaseId" VARCHAR(66) NOT NULL, + "amount" VARCHAR(64) NOT NULL, + "currency" VARCHAR(12) NOT NULL, + "shopId" VARCHAR(66) NOT NULL, + "account" VARCHAR(42) NOT NULL, + "loyaltyType" INTEGER, + "purchaseAmount" VARCHAR(64) NOT NULL, + "feeAmount" VARCHAR(64) NOT NULL, + "totalAmount" VARCHAR(64) NOT NULL, + "paymentStatus" INTEGER, + PRIMARY KEY ("paymentId") + )