npm package diff
Package: @forge/sql
Versions: 2.2.3 - 2.3.0-next.0
Modified:package/out/migration.js
Index: package/out/migration.js
===================================================================
--- package/out/migration.js
+++ package/out/migration.js
@@ -13,9 +13,9 @@
this.sqlClient = sqlClient;
this.migrations = [];
}
async initialize() {
- await this.sqlClient.executeRaw(SCHEMA_VERSION_TABLE_CREATE_QUERY);
+ await this.sqlClient.executeDDL(SCHEMA_VERSION_TABLE_CREATE_QUERY);
}
enqueue(name, statement) {
if (this.migrations.some((migration) => migration.name === name)) {
return this;
@@ -26,9 +26,9 @@
getEnqueued() {
return this.migrations;
}
async list() {
- const result = await this.sqlClient.executeRaw(LIST_MIGRATIONS_QUERY);
+ const result = await this.sqlClient.executeDDL(LIST_MIGRATIONS_QUERY);
const migrations = new Array();
for (const row of result.rows) {
const migratedAt = new Date(row.migratedAt);
const id = row.id;
@@ -43,9 +43,9 @@
const migrationsToRun = this.migrations.filter((migration) => !previousMigrations.some((prev) => prev.name === migration.name));
const migrationsSuccessfullyRun = [];
for (const migration of migrationsToRun) {
try {
- await this.sqlClient.executeRaw(migration.statement);
+ await this.sqlClient.executeDDL(migration.statement);
}
catch (error) {
throw new errors_1.MigrationExecutionError(migration.name, migrationsToRun.map((m) => m.name));
}
Modified:package/out/__test__/migration.test.js
Index: package/out/__test__/migration.test.js
===================================================================
--- package/out/__test__/migration.test.js
+++ package/out/__test__/migration.test.js
@@ -37,27 +37,27 @@
});
describe('list', () => {
it('should list all previously migrations', async () => {
const migratedAtDate = new Date();
- mockSqlClient.executeRaw.mockResolvedValueOnce({
+ mockSqlClient.executeDDL.mockResolvedValueOnce({
rows: [{ id: 1, name: 'create-table-test', migratedAt: migratedAtDate }]
});
const migrations = await migrationRunner.list();
expect(migrations.length).toBe(1);
expect(migrations[0]).toEqual({ id: 1, name: 'create-table-test', migratedAt: migratedAtDate });
- expect(mockSqlClient.executeRaw).toHaveBeenCalledWith('SELECT id, name, migratedAt FROM __migrations;');
+ expect(mockSqlClient.executeDDL).toHaveBeenCalledWith('SELECT id, name, migratedAt FROM __migrations;');
});
it('should be empty when no previous migrations exist', async () => {
- mockSqlClient.executeRaw.mockResolvedValueOnce({
+ mockSqlClient.executeDDL.mockResolvedValueOnce({
rows: []
});
const migrations = await migrationRunner.list();
expect(migrations.length).toBe(0);
- expect(mockSqlClient.executeRaw).toHaveBeenCalledWith('SELECT id, name, migratedAt FROM __migrations;');
+ expect(mockSqlClient.executeDDL).toHaveBeenCalledWith('SELECT id, name, migratedAt FROM __migrations;');
});
it('should raise an error when schema version table does not exist', async () => {
const tableDoesNotExistError = new Error(errorCodes_1.errorCodes.SQL_EXECUTION_ERROR);
- mockSqlClient.executeRaw.mockRejectedValue(tableDoesNotExistError);
+ mockSqlClient.executeDDL.mockRejectedValue(tableDoesNotExistError);
await expect(migrationRunner.list()).rejects.toThrow(tableDoesNotExistError);
});
});
describe('run', () => {
@@ -71,12 +71,12 @@
describe('when few migrations have been successfully run in the past', () => {
it('should execute migrations that have not been previously run', async () => {
migrationRunner.enqueue('v_001_create_table_foo', CREATE_TABLE_FOO_QUERY);
migrationRunner.enqueue('v_002_create_table_bar', CREATE_TABLE_BAR_QUERY);
- (0, jest_when_1.when)(mockSqlClient.executeRaw)
+ (0, jest_when_1.when)(mockSqlClient.executeDDL)
.calledWith((0, jest_when_1.when)((arg) => arg.startsWith('CREATE TABLE IF NOT EXISTS __migrations')))
.mockResolvedValue({ rows: [] });
- (0, jest_when_1.when)(mockSqlClient.executeRaw)
+ (0, jest_when_1.when)(mockSqlClient.executeDDL)
.calledWith('SELECT id, name, migratedAt FROM __migrations;')
.mockResolvedValue({
rows: [{ name: 'v_001_create_table_foo', statement: CREATE_TABLE_FOO_QUERY, migratedAt: new Date() }]
});
@@ -89,20 +89,20 @@
.mockReturnValue(new sql_statement_1.SqlStatement(insertMigrationVersionQueryTemplate, mockApiCall));
const result = await migrationRunner.run();
expect(result).toEqual(['v_002_create_table_bar']);
expect(mockSqlClient.prepare).toHaveBeenCalledTimes(1);
- expect(mockSqlClient.executeRaw).toHaveBeenCalledTimes(3);
+ expect(mockSqlClient.executeDDL).toHaveBeenCalledTimes(3);
});
});
describe('when no migrations have been run in the past', () => {
it('should execute migrations that have not been previously run', async () => {
const CREATE_TABLE_FOO_QUERY = 'CREATE TABLE IF NOT EXISTS foo (id INT)';
migrationRunner.enqueue('v_001_create_table_foo', CREATE_TABLE_FOO_QUERY);
migrationRunner.enqueue('v_002_create_table_bar', 'CREATE TABLE IF NOT EXISTS bar (id INT)');
- (0, jest_when_1.when)(mockSqlClient.executeRaw).calledWith('SELECT id, name, migratedAt FROM __migrations;').mockResolvedValue({
+ (0, jest_when_1.when)(mockSqlClient.executeDDL).calledWith('SELECT id, name, migratedAt FROM __migrations;').mockResolvedValue({
rows: []
});
- (0, jest_when_1.when)(mockSqlClient.executeRaw)
+ (0, jest_when_1.when)(mockSqlClient.executeDDL)
.calledWith((0, jest_when_1.when)((arg) => arg.startsWith('CREATE TABLE IF NOT EXISTS __migrations')))
.mockResolvedValue({ rows: [] });
const mockApiCall = jest.fn();
(0, jest_when_1.when)(mockApiCall)
@@ -113,9 +113,9 @@
.mockReturnValue(new sql_statement_1.SqlStatement(insertMigrationVersionQueryTemplate, mockApiCall));
const result = await migrationRunner.run();
expect(result).toEqual(['v_001_create_table_foo', 'v_002_create_table_bar']);
expect(mockSqlClient.prepare).toHaveBeenCalledTimes(2);
- expect(mockSqlClient.executeRaw).toHaveBeenCalledTimes(4);
+ expect(mockSqlClient.executeDDL).toHaveBeenCalledTimes(4);
});
});
});
});
Modified:package/out/sql.js
Index: package/out/sql.js
===================================================================
--- package/out/sql.js
+++ package/out/sql.js
@@ -1,11 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
-exports.sql = exports.SqlClient = void 0;
+exports.sql = exports.SqlClient = exports.SQL_API_ENDPOINTS = void 0;
const api_1 = require("@forge/api");
const sql_statement_1 = require("./sql-statement");
const error_handling_1 = require("./utils/error-handling");
const errors_1 = require("./errors");
+exports.SQL_API_ENDPOINTS = {
+ EXECUTE: '/api/v1/execute',
+ EXECUTE_DDL: '/api/v1/execute/ddl'
+};
class SqlClient {
async sendRequest(path, options) {
const response = await (0, api_1.__fetchProduct)({ provider: 'app', remote: 'sql' })(path, {
...options,
@@ -16,10 +20,10 @@
}
});
return response;
}
- async storageApi(query, params = [], method = 'all') {
- const response = await this.sendRequest('api/v1/execute', {
+ async storageApi(query, params = [], method = 'all', endpoint = exports.SQL_API_ENDPOINTS.EXECUTE) {
+ const response = await this.sendRequest(endpoint, {
method: 'POST',
body: JSON.stringify({ query, params, method })
});
await (0, error_handling_1.checkResponseError)(response);
@@ -30,19 +34,27 @@
catch (error) {
throw new errors_1.ForgeSQLError(`Unexpected error. Response was not valid JSON: ${responseText}`);
}
}
- prepare(query) {
- return new sql_statement_1.SqlStatement(query, this.storageApi.bind(this));
+ async storageApiWithOptions(query, options = {}) {
+ const { endpoint = exports.SQL_API_ENDPOINTS.EXECUTE, params = [], method = 'all' } = options;
+ return await this.storageApi(query, params, method, endpoint);
}
+ prepare(query, endpoint = exports.SQL_API_ENDPOINTS.EXECUTE) {
+ const remoteSqlApi = (query, params, method) => this.storageApiWithOptions(query, { endpoint, params, method });
+ return new sql_statement_1.SqlStatement(query, remoteSqlApi);
+ }
async executeRaw(query) {
- return await this.prepare(query).execute();
+ return await this.prepare(query, exports.SQL_API_ENDPOINTS.EXECUTE).execute();
}
async _provision() {
const response = await this.sendRequest('/api/v1/provision', {
method: 'POST'
});
await (0, error_handling_1.checkResponseError)(response, 'Unexpected error in provision request');
}
+ async executeDDL(query) {
+ return await this.prepare(query, exports.SQL_API_ENDPOINTS.EXECUTE_DDL).execute();
+ }
}
exports.SqlClient = SqlClient;
exports.sql = new SqlClient();
Modified:package/out/__test__/sql.test.js
Index: package/out/__test__/sql.test.js
===================================================================
--- package/out/__test__/sql.test.js
+++ package/out/__test__/sql.test.js
@@ -20,13 +20,63 @@
const response = new node_fetch_1.Response(JSON.stringify(body), { status: 200 });
mockFetch.mockResolvedValue(response);
return body;
}
+ describe('storageApi', () => {
+ it('should send a request and return the response body', async () => {
+ mockFetchExecute([]);
+ const result = await sqlClient['storageApi']('SELECT * FROM test', [], undefined, sql_1.SQL_API_ENDPOINTS.EXECUTE);
+ expect(mockFetch).toHaveBeenCalledWith('/api/v1/execute', {
+ method: 'POST',
+ body: JSON.stringify({ query: 'SELECT * FROM test', params: [], method: 'all' }),
+ redirect: 'follow',
+ headers: { 'Content-Type': 'application/json' }
+ });
+ expect(result).toEqual({ rows: [] });
+ });
+ it('should send a request with parameters and method', async () => {
+ mockFetchExecute([]);
+ const params = [1];
+ const result = await sqlClient['storageApi']('SELECT * FROM test WHERE id = ?', params, 'one', sql_1.SQL_API_ENDPOINTS.EXECUTE);
+ expect(mockFetch).toHaveBeenCalledWith('/api/v1/execute', {
+ method: 'POST',
+ body: JSON.stringify({ query: 'SELECT * FROM test WHERE id = ?', params, method: 'one' }),
+ redirect: 'follow',
+ headers: { 'Content-Type': 'application/json' }
+ });
+ expect(result).toEqual({ rows: [] });
+ });
+ it('should send a request to DDL endpoint with method', async () => {
+ mockFetchExecute([]);
+ const result = await sqlClient['storageApi']('CREATE TABLE test (id INT)', [], 'one', sql_1.SQL_API_ENDPOINTS.EXECUTE_DDL);
+ expect(mockFetch).toHaveBeenCalledWith('/api/v1/execute/ddl', {
+ method: 'POST',
+ body: JSON.stringify({ query: 'CREATE TABLE test (id INT)', params: [], method: 'one' }),
+ redirect: 'follow',
+ headers: { 'Content-Type': 'application/json' }
+ });
+ expect(result).toEqual({ rows: [] });
+ });
+ it('should handle invalid JSON body', async () => {
+ const responseText = 'Invalid JSON';
+ const response = new node_fetch_1.Response(responseText, { status: 200 });
+ mockFetch.mockResolvedValue(response);
+ await expect(sqlClient['storageApi']('SELECT * from strange;', [], 'one', sql_1.SQL_API_ENDPOINTS.EXECUTE)).rejects.toThrow(`Unexpected error. Response was not valid JSON: ${responseText}`);
+ });
+ it('should throw ForgeSQLAPIError on API error', async () => {
+ const forgeError = { code: 'INVALID_QUERY', message: 'Invalid SQL query' };
+ const mockResponse = new node_fetch_1.Response(JSON.stringify(forgeError), {
+ status: 400
+ });
+ mockFetch.mockResolvedValue(mockResponse);
+ await expect(sqlClient['storageApi']('INVALID SQL QUERY', [], undefined, sql_1.SQL_API_ENDPOINTS.EXECUTE)).rejects.toThrow(new errors_1.ForgeSQLAPIError({ status: mockResponse.status, statusText: mockResponse.statusText }, forgeError));
+ });
+ });
describe('sendRequest', () => {
it('should send a request with the correct options and return the response', async () => {
const mockResponse = { ok: true, status: 200 };
mockFetch.mockResolvedValue(mockResponse);
- const path = 'api/v1/execute';
+ const path = '/api/v1/execute';
const options = { method: 'GET', headers: { 'Custom-Header': 'value' } };
const response = await sqlClient['sendRequest'](path, options);
expect(mockFetch).toHaveBeenCalledWith(path, {
...options,
@@ -40,9 +90,9 @@
});
it('should handle requests without options', async () => {
const mockResponse = { ok: true, status: 200 };
mockFetch.mockResolvedValue(mockResponse);
- const path = 'api/v1/execute';
+ const path = '/api/v1/execute';
const response = await sqlClient['sendRequest'](path);
expect(mockFetch).toHaveBeenCalledWith(path, {
redirect: 'follow',
headers: {
@@ -53,52 +103,13 @@
});
it('should propagate fetch errors', async () => {
const mockError = new Error('Network error');
mockFetch.mockRejectedValue(mockError);
- const path = 'api/v1/execute';
+ const path = '/api/v1/execute';
await expect(sqlClient['sendRequest'](path)).rejects.toThrow(mockError);
expect(mockFetch).toHaveBeenCalledWith(path, expect.any(Object));
});
});
- describe('storageApi', () => {
- it('should send a request and return the response body', async () => {
- mockFetchExecute([]);
- const result = await sqlClient.storageApi('SELECT * FROM test');
- expect(mockFetch).toHaveBeenCalledWith('api/v1/execute', {
- method: 'POST',
- body: JSON.stringify({ query: 'SELECT * FROM test', params: [], method: 'all' }),
- redirect: 'follow',
- headers: { 'Content-Type': 'application/json' }
- });
- expect(result).toEqual({ rows: [] });
- });
- it('should send a request with parameters and method', async () => {
- mockFetchExecute([]);
- const params = [1];
- const result = await sqlClient.storageApi('SELECT * FROM test WHERE id = ?', params, 'one');
- expect(mockFetch).toHaveBeenCalledWith('api/v1/execute', {
- method: 'POST',
- body: JSON.stringify({ query: 'SELECT * FROM test WHERE id = ?', params, method: 'one' }),
- redirect: 'follow',
- headers: { 'Content-Type': 'application/json' }
- });
- expect(result).toEqual({ rows: [] });
- });
- it('should handle invalid JSON body', async () => {
- const responseText = 'Invalid JSON';
- const response = new node_fetch_1.Response(responseText, { status: 200 });
- mockFetch.mockResolvedValue(response);
- await expect(sqlClient.storageApi('SELECT * from strange;')).rejects.toThrow(`Unexpected error. Response was not valid JSON: ${responseText}`);
- });
- it('should throw ForgeSQLAPIError on API error', async () => {
- const forgeError = { code: 'INVALID_QUERY', message: 'Invalid SQL query' };
- const mockResponse = new node_fetch_1.Response(JSON.stringify(forgeError), {
- status: 400
- });
- mockFetch.mockResolvedValue(mockResponse);
- await expect(sqlClient.storageApi('INVALID SQL QUERY')).rejects.toThrow(new errors_1.ForgeSQLAPIError({ status: mockResponse.status, statusText: mockResponse.statusText }, forgeError));
- });
- });
describe('prepare', () => {
it('should return a SqlStatement instance with query', () => {
const statement = sqlClient.prepare('INSERT INTO test VALUES (?, ?)');
expect(statement).toBeInstanceOf(sql_statement_1.SqlStatement);
@@ -155,5 +166,26 @@
mockFetch.mockResolvedValue(mockResponse);
await expect(sqlClient._provision()).rejects.toThrow('Unexpected error in provision request');
});
});
+ describe('executeDDL', () => {
+ it('should return a valid Result object when DDL query gexecuted successfully', async () => {
+ const mockResponse = {
+ ok: true,
+ status: 200,
+ text: jest.fn().mockResolvedValue(JSON.stringify({ success: true }))
+ };
+ mockFetch.mockResolvedValue(mockResponse);
+ const result = await sqlClient.executeDDL('CREATE TABLE test (id INT)');
+ expect(result).toEqual({ success: true });
+ expect(mockFetch).toHaveBeenCalledWith('/api/v1/execute/ddl', expect.objectContaining({ method: 'POST' }));
+ });
+ it('should throw ForgeSQLError when response is not valid JSON', async () => {
+ const forgeError = { code: 'INVALID_QUERY', message: 'Invalid SQL query' };
+ const mockResponse = new node_fetch_1.Response(JSON.stringify(forgeError), {
+ status: 400
+ });
+ mockFetch.mockResolvedValue(mockResponse);
+ await expect(sqlClient.executeDDL('INVALID SQL QUERY')).rejects.toThrow(new errors_1.ForgeSQLAPIError(mockResponse, forgeError));
+ });
+ });
});
Modified:package/package.json
Index: package/package.json
===================================================================
--- package/package.json
+++ package/package.json
@@ -1,7 +1,7 @@
{
"name": "@forge/sql",
- "version": "2.2.3",
+ "version": "2.3.0-next.0",
"description": "Forge SQL sdk",
"author": "Atlassian",
"license": "UNLICENSED",
"main": "out/index.js",
Modified:package/out/utils/error-handling.d.ts.map
Index: package/out/utils/error-handling.d.ts.map
===================================================================
--- package/out/utils/error-handling.d.ts.map
+++ package/out/utils/error-handling.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAA2B,UAAU,EAAoB,MAAM,WAAW,CAAC;AAGlF,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAO9D;AASD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC/F"}
\ No newline at end of file
+{"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAA2B,UAAU,EAAoB,MAAM,WAAW,CAAC;AAGlF,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAO9D;AASD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC/F"}
\ No newline at end of file
Modified:package/out/sql.d.ts.map
Index: package/out/sql.d.ts.map
===================================================================
--- package/out/sql.d.ts.map
+++ package/out/sql.d.ts.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI9D,qBAAa,SAAS;YACN,WAAW;IAYnB,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,aAAkB,EAAE,MAAM,SAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAiBhH,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAI1D,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAK9D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CASlC;AAED,eAAO,MAAM,GAAG,WAAkB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAgC,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI7E,eAAO,MAAM,iBAAiB;;;CAGpB,CAAC;AAEX,aAAK,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAElF,qBAAa,SAAS;YACN,WAAW;YAYX,UAAU;YAsBV,qBAAqB;IAYnC,OAAO,CAAC,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,eAA2C,GACpD,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAU3B,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAK9D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,UAAU,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;CAGrE;AAED,eAAO,MAAM,GAAG,WAAkB,CAAC"}
\ No newline at end of file
Modified:package/out/sql.d.ts
Index: package/out/sql.d.ts
===================================================================
--- package/out/sql.d.ts
+++ package/out/sql.d.ts
@@ -1,11 +1,19 @@
import { Result } from './utils/types';
-import { SqlParameters, SqlStatement } from './sql-statement';
+import { SqlStatement } from './sql-statement';
+export declare const SQL_API_ENDPOINTS: {
+ readonly EXECUTE: "/api/v1/execute";
+ readonly EXECUTE_DDL: "/api/v1/execute/ddl";
+};
+declare type SqlAPIEndPoints = (typeof SQL_API_ENDPOINTS)[keyof typeof SQL_API_ENDPOINTS];
export declare class SqlClient {
private sendRequest;
- storageApi<DataType>(query: string, params?: SqlParameters, method?: string): Promise<Result<DataType>>;
- prepare<DataType>(query: string): SqlStatement<Result<DataType>>;
+ private storageApi;
+ private storageApiWithOptions;
+ prepare<DataType>(query: string, endpoint?: SqlAPIEndPoints): SqlStatement<Result<DataType>>;
executeRaw<DataType>(query: string): Promise<Result<DataType>>;
_provision(): Promise<void>;
+ executeDDL<DataType>(query: string): Promise<Result<DataType>>;
}
export declare const sql: SqlClient;
+export {};
//# sourceMappingURL=sql.d.ts.map
\ No newline at end of file