From 5c87f499bb617090e161ae8e378bf649d303da1f Mon Sep 17 00:00:00 2001 From: Pavlo Date: Mon, 3 Jan 2022 09:48:45 +0000 Subject: [PATCH 01/10] set default request fields to undefined --- package.json | 2 +- src/sql-data.api.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 83e9938..0fb5a55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.1", + "version": "0.3.2", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 0a06948..3eaf772 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -818,14 +818,14 @@ export class SqlDataApi { : undefined; const request = { - select: fields, - filterString: queryInfo.filter, - filterParameters: filterParams, - skip: queryInfo.skip, - top: queryInfo.top, - orderBy: queryInfo.orderBy, - mainTableAlias: mainTable.alias || queryInfo.mainTableAlias, - tablesJoin, + select: fields?.length ? fields : undefined, + filterString: queryInfo.filter || undefined, + filterParameters: filterParams || undefined, + skip: queryInfo.skip || undefined, + top: queryInfo.top || undefined, + orderBy: queryInfo.orderBy || undefined, + mainTableAlias: mainTable.alias || queryInfo.mainTableAlias || undefined, + tablesJoin: tablesJoin?.length ? tablesJoin : undefined, } as RequestSqlQueryInfo; if (!this.connectionName?.length) { From b7fed43aa609e5a5421a84ed54394428769d35c1 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Wed, 20 Jul 2022 17:27:34 +0100 Subject: [PATCH 02/10] changed http* utility methods --- package.json | 2 +- src/sql-data.api.ts | 130 ++++++++++++++++++++++++++++++++------------ 2 files changed, 96 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 0fb5a55..8e69b86 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.2", + "version": "0.3.3", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 3eaf772..903d546 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -158,14 +158,18 @@ export async function authenticate( username: string, password: string ): Promise { - SqlDataApi.BearerToken = ( - (await httpRequest( - "POST", - `${SqlDataApi.BaseUrl}/api/security/authenticate`, - { username, password }, - { headers: appHttpHeaders } - )) as { token: string } - ).token; + const res = await httpRequest( + "POST", + `${SqlDataApi.BaseUrl}/api/security/authenticate`, + { username, password }, + { headers: appHttpHeaders } + ); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + SqlDataApi.BearerToken = (res.data as { token: string }).token; return true; } @@ -199,7 +203,7 @@ export function httpRequest( url: string, body?: TRequest, config?: Record -): Promise { +): Promise> { const requestConfig: AxiosRequestConfig = { method, url, ...(config || {}) }; if (body) { @@ -213,23 +217,33 @@ export function httpRequest( } return axios.request(requestConfig).then( - (r: ServerResponse): TResponse => r.data, + (r: ServerResponse): ServerResponse => ({ + data: r.data, + isOk: true, + status: r.status, + statusText: r.statusText, + }), (e) => { // making an error more informative. // this was a reason why we switched to axios, as it gives us a real exception details, // beyond a statusText const response = e.response || {}; - let errMessage = + let errorMessage = (response.data || {}).message || response.data || response.statusText || "Http Connection Error"; - if (typeof errMessage === "object") { - errMessage = JSON.stringify(errMessage); + if (typeof errorMessage === "object") { + errorMessage = JSON.stringify(errorMessage); } - const err = Error(errMessage); - Object.assign(err, response); - throw err; + + return { + isOk: false, + status: response.status, + statusText: response.statusText, + data: null as any, + errorMessage, + }; } ); } @@ -237,14 +251,14 @@ export function httpRequest( export function httpGet( url: string, config?: Record -): Promise { +): Promise> { return httpRequest("GET", url, null, config); } export function httpGetText( url: string, config?: Record -): Promise { +): Promise> { const headers = config?.headers || {}; headers["Content-Type"] = "text/plain"; @@ -261,7 +275,7 @@ export function httpPost( url: string, body: TRequest, config?: Record -): Promise { +): Promise> { return httpRequest("POST", url, body, config); } @@ -269,7 +283,7 @@ export function httpPut( url: string, body: TRequest, config?: Record -): Promise { +): Promise> { return httpRequest("PUT", url, body, config); } @@ -277,7 +291,7 @@ export function httpDelete( url: string, body: TRequest, config?: Record -): Promise { +): Promise> { return httpRequest("DELETE", url, body, config); } @@ -439,9 +453,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const result = (await httpRequest("POST", url, dto, { + const res = await httpRequest("POST", url, dto, { headers: { ...appHttpHeaders, ...headers }, - })) as number; + }); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + const result = res.data as number; return result; } @@ -470,9 +490,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const result = (await httpRequest("POST", url, dto, { + const res = await httpRequest("POST", url, dto, { headers: { ...appHttpHeaders, ...headers }, - })) as number; + }); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + const result = res.data as number; return result; } @@ -543,9 +569,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const result = (await httpRequest("POST", url, dto, { + const res = await httpRequest("POST", url, dto, { headers: { ...appHttpHeaders, ...headers }, - })) as number; + }); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + const result = res.data as number; return result; } @@ -587,9 +619,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const result = (await httpRequest("POST", url, dto, { + const res = await httpRequest("POST", url, dto, { headers: { ...appHttpHeaders, ...headers }, - })) as SqlQueryResponse; + }); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + const result = res.data as SqlQueryResponse; return result; } @@ -642,12 +680,18 @@ export class SqlDataApi { if (!items || !items.length) { if (itemsToDelete?.length) { - return (await httpRequest( + const res = await httpRequest( "POST", url, { itemsToDelete }, { headers: { ...appHttpHeaders, ...headersValue } } - )) as SqlSaveStatus; + ); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + return res.data as SqlSaveStatus; } else { return status; } @@ -691,9 +735,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const singleStatus = (await httpRequest("POST", url, body, { + const res = await httpRequest("POST", url, body, { headers: Object.assign(appHttpHeaders, headersValue), - })) as SqlSaveStatus; + }); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + const singleStatus = res.data as SqlSaveStatus; if (typeof batchSavedFunc === "function") { batchSavedFunc(currentIndex + 1, singleStatus); @@ -844,9 +894,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const response = (await httpRequest("POST", url, request, { + const res = await httpRequest("POST", url, request, { headers: { ...appHttpHeaders, ...headers }, - })) as SqlQueryResponse; + }); + + if (res.errorMessage) { + throw new Error(res.errorMessage); + } + + const response = res.data as SqlQueryResponse; return response.table as TableDto; } } @@ -860,6 +916,10 @@ interface ExecuteSqlDto { interface ServerResponse { data: T; + isOk?: boolean; + status: number; + statusText: string; + errorMessage?: string; } interface RequestSqlQueryInfo { From e1b28122f336431d29bec94abc8ce62aeca5bf33 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 21 Jul 2022 20:16:01 +0100 Subject: [PATCH 03/10] rolledback http* calls but httpRequest --- package.json | 2 +- src/sql-data.api.ts | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 8e69b86..9d1fedd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.3", + "version": "0.3.5", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 903d546..5cd320e 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -251,14 +251,19 @@ export function httpRequest( export function httpGet( url: string, config?: Record -): Promise> { - return httpRequest("GET", url, null, config); +): Promise { + return httpRequest("GET", url, null, config).then((r) => { + if (!r.errorMessage) { + return r.data as TResponse; + } + throw new Error(r.errorMessage); + }); } export function httpGetText( url: string, config?: Record -): Promise> { +): Promise { const headers = config?.headers || {}; headers["Content-Type"] = "text/plain"; @@ -268,31 +273,51 @@ export function httpGetText( config.headers = headers; - return httpRequest("GET", url, null, config); + return httpRequest("GET", url, null, config).then((r) => { + if (!r.errorMessage) { + return r.data as string; + } + throw new Error(r.errorMessage); + }); } export function httpPost( url: string, body: TRequest, config?: Record -): Promise> { - return httpRequest("POST", url, body, config); +): Promise { + return httpRequest("POST", url, body, config).then((r) => { + if (!r.errorMessage) { + return r.data as TResponse; + } + throw new Error(r.errorMessage); + }); } export function httpPut( url: string, body: TRequest, config?: Record -): Promise> { - return httpRequest("PUT", url, body, config); +): Promise { + return httpRequest("PUT", url, body, config).then((r) => { + if (!r.errorMessage) { + return r.data as TResponse; + } + throw new Error(r.errorMessage); + }); } export function httpDelete( url: string, body: TRequest, config?: Record -): Promise> { - return httpRequest("DELETE", url, body, config); +): Promise { + return httpRequest("DELETE", url, body, config).then((r) => { + if (!r.errorMessage) { + return r.data as TResponse; + } + throw new Error(r.errorMessage); + }); } /** From 7136d0156a6b6d841235719a8d45d60cae099a28 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 3 Nov 2022 21:07:32 +0000 Subject: [PATCH 04/10] added sybase ase support --- package.json | 2 +- src/db-type-converter-maps.ts | 34 ++++++- src/db-type-converter.ts | 99 +++++++++++++------- src/db-types.ts | 169 ++++++++++++++++++++-------------- 4 files changed, 202 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index 9d1fedd..ee95b60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.5", + "version": "0.3.6", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/db-type-converter-maps.ts b/src/db-type-converter-maps.ts index 5bd97d4..5f47817 100644 --- a/src/db-type-converter-maps.ts +++ b/src/db-type-converter-maps.ts @@ -1,5 +1,5 @@ import { DataTypeName } from "datapipe-js"; -import { PostgreSqlDataTypes, SQLDataTypes } from "./db-types"; +import { PostgreSqlDataTypes, SQLDataTypes, SybaseAseDataTypes } from "./db-types"; export const SqlTypesToDataTypesMap: Record = { @@ -23,6 +23,27 @@ export const SqlTypesToDataTypesMap: Record = { [SQLDataTypes.BIT]: DataTypeName.Boolean }; +export const SybaseAseTypesToDataTypesMap: Record = { + [SybaseAseDataTypes.TEXT]: DataTypeName.LargeString, + [SybaseAseDataTypes.CHAR]: DataTypeName.String, + [SybaseAseDataTypes.VARCHAR]: DataTypeName.String, + [SybaseAseDataTypes.NVARCHAR]: DataTypeName.String, + [SybaseAseDataTypes.BINARY]: DataTypeName.LargeString, + [SybaseAseDataTypes.VARBINARY]: DataTypeName.LargeString, + [SybaseAseDataTypes.DATE]: DataTypeName.Date, + [SybaseAseDataTypes.SMALLDATETIME]: DataTypeName.DateTime, + [SybaseAseDataTypes.DATETIME]: DataTypeName.DateTime, + [SybaseAseDataTypes.DECIMAL]: DataTypeName.FloatNumber, + [SybaseAseDataTypes.FLOAT]: DataTypeName.FloatNumber, + [SybaseAseDataTypes.MONEY]: DataTypeName.FloatNumber, + [SybaseAseDataTypes.NUMERIC]: DataTypeName.FloatNumber, + [SybaseAseDataTypes.REAL]: DataTypeName.FloatNumber, + [SybaseAseDataTypes.SMALLINT]: DataTypeName.WholeNumber, + [SybaseAseDataTypes.BIGINT]: DataTypeName.BigIntNumber, + [SybaseAseDataTypes.INT]: DataTypeName.WholeNumber, + [SybaseAseDataTypes.TINYINT]: DataTypeName.WholeNumber, + [SybaseAseDataTypes.BIT]: DataTypeName.Boolean +}; export const PostgreSqlTypesToDataTypesMap: Record = { [PostgreSqlDataTypes.BOOLEAN]: DataTypeName.Boolean, [PostgreSqlDataTypes.SMALLINT]: DataTypeName.WholeNumber, @@ -65,4 +86,15 @@ export const DataTypeToSqlTypesMap: Record = { [DataTypeName.Boolean]: SQLDataTypes.BIT, [DataTypeName.BigIntNumber]: SQLDataTypes.BIGINT, [DataTypeName.LargeString]: SQLDataTypes.TEXT +}; + +export const DataTypeToSybaseAseTypesMap: Record = { + [DataTypeName.WholeNumber]: SybaseAseDataTypes.INT, + [DataTypeName.Date]: SybaseAseDataTypes.DATE, + [DataTypeName.DateTime]: SybaseAseDataTypes.DATETIME, + [DataTypeName.FloatNumber]: SybaseAseDataTypes.FLOAT, + [DataTypeName.String]: SybaseAseDataTypes.VARCHAR, + [DataTypeName.Boolean]: SybaseAseDataTypes.BIT, + [DataTypeName.BigIntNumber]: SybaseAseDataTypes.BIGINT, + [DataTypeName.LargeString]: SybaseAseDataTypes.TEXT }; \ No newline at end of file diff --git a/src/db-type-converter.ts b/src/db-type-converter.ts index e3a5749..5355f6a 100644 --- a/src/db-type-converter.ts +++ b/src/db-type-converter.ts @@ -1,48 +1,85 @@ import { DataTypeName } from "datapipe-js"; import { - DataTypeToPostgreSqlTypesMap, DataTypeToSqlTypesMap, - PostgreSqlTypesToDataTypesMap, SqlTypesToDataTypesMap + DataTypeToPostgreSqlTypesMap, + DataTypeToSqlTypesMap, + DataTypeToSybaseAseTypesMap, + PostgreSqlTypesToDataTypesMap, + SqlTypesToDataTypesMap, + SybaseAseTypesToDataTypesMap, } from "./db-type-converter-maps"; -import { DbConnectionType, PostgreSqlDataTypes, SQLDataTypes } from "./db-types"; +import { + DbConnectionType, + PostgreSqlDataTypes, + SQLDataTypes, + SybaseAseDataTypes, +} from "./db-types"; export class DbTypeConverter { - toDbType(dbType: DataTypeName, connectionType: DbConnectionType): SQLDataTypes | PostgreSqlDataTypes { - if (connectionType === 'SqlServer' || connectionType === 'SqlServerSchema'){ - return this.toSqlServerType(dbType); - } - - if (connectionType === 'PostgreSql'){ - return this.toPostgreSqlType(dbType); - } + toDbType( + dbType: DataTypeName, + connectionType: DbConnectionType + ): SQLDataTypes | PostgreSqlDataTypes | SybaseAseDataTypes { + if ( + connectionType === "SqlServer" || + connectionType === "SqlServerSchema" + ) { + return this.toSqlServerType(dbType); + } - throw new Error('Not Supported ConnectionType'); + if (connectionType === "PostgreSql") { + return this.toPostgreSqlType(dbType); } - fromDbType(dbType: SQLDataTypes | PostgreSqlDataTypes, connectionType: DbConnectionType): DataTypeName { - if (connectionType === 'SqlServer' || connectionType === 'SqlServerSchema'){ - return this.fromSqlServerType(dbType as SQLDataTypes); - } + if (connectionType === "SybaseAse") { + return this.toSybaseAseSqlType(dbType); + } - if (connectionType === 'PostgreSql'){ - return this.fromPostgreSqlType(dbType as PostgreSqlDataTypes); - } + throw new Error("Not Supported ConnectionType"); + } - throw new Error('Not Supported ConnectionType'); + fromDbType( + dbType: SQLDataTypes | PostgreSqlDataTypes | SybaseAseDataTypes, + connectionType: DbConnectionType + ): DataTypeName { + if ( + connectionType === "SqlServer" || + connectionType === "SqlServerSchema" + ) { + return this.fromSqlServerType(dbType as SQLDataTypes); } - toSqlServerType(dbType: DataTypeName): SQLDataTypes { - return DataTypeToSqlTypesMap[dbType]; + if (connectionType === "PostgreSql") { + return this.fromPostgreSqlType(dbType as PostgreSqlDataTypes); } - toPostgreSqlType(dbType: DataTypeName): PostgreSqlDataTypes { - return DataTypeToPostgreSqlTypesMap[dbType]; + if (connectionType === "SybaseAse") { + return this.fromSybaseAseSqlType(dbType as SybaseAseDataTypes); } - fromSqlServerType(dbType: SQLDataTypes): DataTypeName { - return SqlTypesToDataTypesMap[dbType]; - } + throw new Error("Not Supported ConnectionType"); + } - fromPostgreSqlType(dbType: PostgreSqlDataTypes): DataTypeName { - return PostgreSqlTypesToDataTypesMap[dbType]; - } -} \ No newline at end of file + toSqlServerType(dbType: DataTypeName): SQLDataTypes { + return DataTypeToSqlTypesMap[dbType]; + } + + toSybaseAseSqlType(dbType: DataTypeName): SybaseAseDataTypes { + return DataTypeToSybaseAseTypesMap[dbType]; + } + + toPostgreSqlType(dbType: DataTypeName): PostgreSqlDataTypes { + return DataTypeToPostgreSqlTypesMap[dbType]; + } + + fromSqlServerType(dbType: SQLDataTypes): DataTypeName { + return SqlTypesToDataTypesMap[dbType]; + } + + fromSybaseAseSqlType(dbType: SybaseAseDataTypes): DataTypeName { + return SybaseAseTypesToDataTypesMap[dbType]; + } + + fromPostgreSqlType(dbType: PostgreSqlDataTypes): DataTypeName { + return PostgreSqlTypesToDataTypesMap[dbType]; + } +} diff --git a/src/db-types.ts b/src/db-types.ts index 46f8049..20ac3c7 100644 --- a/src/db-types.ts +++ b/src/db-types.ts @@ -1,79 +1,110 @@ -export type DbConnectionType = 'SqlServer' | 'SqlServerSchema' | 'PostgreSql' | 'Oracle' | 'MySql'; +export type DbConnectionType = + | "SqlServer" + | "SqlServerSchema" + | "PostgreSql" + | "Oracle" + | "MySql" + | "SybaseAse"; export enum SQLDataTypes { - VARCHAR = 'varchar', - NVARCHAR = 'nvarchar', - FLOAT = 'float', - DATE = 'date', - DATETIME2 = 'datetime2', - DATETIME = 'datetime', - INT = 'int', + VARCHAR = "varchar", + NVARCHAR = "nvarchar", + FLOAT = "float", + DATE = "date", + DATETIME2 = "datetime2", + DATETIME = "datetime", + INT = "int", - TEXT = 'text', - CHAR = 'char', - BINARY = 'binary', - VARBINARY = 'varbinary', + TEXT = "text", + CHAR = "char", + BINARY = "binary", + VARBINARY = "varbinary", - BIT = 'bit', - TINYINT = 'tinyint', - SMALLINT = 'smallint', - BIGINT = 'bigint', - DECIMAL = 'decimal', - REAL = 'real', - NUMERIC = 'numeric', + BIT = "bit", + TINYINT = "tinyint", + SMALLINT = "smallint", + BIGINT = "bigint", + DECIMAL = "decimal", + REAL = "real", + NUMERIC = "numeric", +} + +export enum SybaseAseDataTypes { + VARCHAR = "varchar", + NVARCHAR = "nvarchar", + FLOAT = "float", + DATE = "date", + DATETIME = "datetime", + SMALLDATETIME = "smalldatetime", + + INT = "int", + + TEXT = "text", + CHAR = "char", + BINARY = "binary", + VARBINARY = "varbinary", + + BIT = "bit", + TINYINT = "tinyint", + SMALLINT = "smallint", + BIGINT = "bigint", + DECIMAL = "decimal", + REAL = "real", + NUMERIC = "numeric", + MONEY = "money", } export enum PostgreSqlDataTypes { - VARCHAR = 'varchar', - INTEGER = 'int', - DOUBLE = 'float8', - DATE = 'date', - TIMESTAMP = 'timestamp', - BOOLEAN = 'bool', - SMALLINT = 'int2', - BIGINT = 'int8', - REAL = 'float4', - INTEGER4 = 'int4', - NUMERIC = 'numeric', - MONEY = 'money', - TEXT = 'text', - CHAR = 'char', - BPCHAR = 'bpchar', - JSONB = 'jsonb', - XML = 'xml', - JSON = 'json' + VARCHAR = "varchar", + INTEGER = "int", + DOUBLE = "float8", + DATE = "date", + TIMESTAMP = "timestamp", + BOOLEAN = "bool", + SMALLINT = "int2", + BIGINT = "int8", + REAL = "float4", + INTEGER4 = "int4", + NUMERIC = "numeric", + MONEY = "money", + TEXT = "text", + CHAR = "char", + BPCHAR = "bpchar", + JSONB = "jsonb", + XML = "xml", + JSON = "json", - // , CITEXT = 'Citext', + // , CITEXT = 'Citext', - // POINT = 'Point', - // LSEG = 'LSeg', - // PATH = 'Path', - // POLYGON = 'Polygon', - // LINE = 'Line', - // CIRCLE = 'Circle', - // BOX = 'Box', - // BIT = 'Bit', - // VARBIT = 'Varbit', - // HSTORE = 'Hstore', - // UUID = 'Uuid', - // CIDR = 'Cidr', - // INET = 'Inet', - // MACADDR = 'MacAddr', - // TSQUERY = 'TsQuery', - // TSVECTOR = 'TsVector', + // POINT = 'Point', + // LSEG = 'LSeg', + // PATH = 'Path', + // POLYGON = 'Polygon', + // LINE = 'Line', + // CIRCLE = 'Circle', + // BOX = 'Box', + // BIT = 'Bit', + // VARBIT = 'Varbit', + // HSTORE = 'Hstore', + // UUID = 'Uuid', + // CIDR = 'Cidr', + // INET = 'Inet', + // MACADDR = 'MacAddr', + // TSQUERY = 'TsQuery', + // TSVECTOR = 'TsVector', - // TIMESTAMPTZ = 'TimestampTz', - // TIME = 'Time', - // TIMETZ = 'TimeTz', - // BYTEA = 'Bytea', - // OID = 'Oid', - // XID = 'Xid', - // CID = 'Cid', - // OIDVECTOR = 'Oidvector', - // NAME = 'Name', - // INTERNALCHAR = 'InternalChar', - // COMPOSITE = 'Composite', - // RANGE = 'Range', - // ENUM = 'Enum', - // ARRAY = 'Array' -}; + // TIMESTAMPTZ = 'TimestampTz', + // TIME = 'Time', + // TIMETZ = 'TimeTz', + // BYTEA = 'Bytea', + // OID = 'Oid', + // XID = 'Xid', + // CID = 'Cid', + // OIDVECTOR = 'Oidvector', + // NAME = 'Name', + // INTERNALCHAR = 'InternalChar', + // COMPOSITE = 'Composite', + // RANGE = 'Range', + // ENUM = 'Enum', + // ARRAY = 'Array' +} From 58920e50b90c30cafc9fb395128167bfced16dfb Mon Sep 17 00:00:00 2001 From: Pavlo Date: Sat, 5 Nov 2022 12:43:33 +0000 Subject: [PATCH 05/10] added string dto convention --- package.json | 2 +- src/sql-data.api.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ee95b60..657fea1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.6", + "version": "0.3.7", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 5cd320e..47d5c06 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -793,7 +793,7 @@ export class SqlDataApi { if (Array.isArray(val)) { return val.map((v) => scalarToPrimitive(v)) as PrimitiveType[]; } - return val instanceof Date ? dateToString(val) : val; + return val instanceof Date ? `dt(${dateToString(val)})` : val; }; const result: PrimitivesObject = {}; From 6857818744e8b2ce1dad91e952b388a35e04c3c4 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Wed, 22 Mar 2023 14:40:05 +0000 Subject: [PATCH 06/10] added joinCondition2 --- README.md | 2 +- package.json | 2 +- src/db-types.ts | 15 +++++++++++++++ src/sql-data.api.ts | 30 ++++++++++-------------------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 72349cf..c247f38 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ There are several ways you can define a query to the SQL Database. But, eventual - **filter** - defines a filter expression e.g. `country = 'uk' and city = 'London'` or you can use parameters and have filter as `country = @country AND city = @city` and provide parameters as an object`{country: 'UK', city: 'London'}`. And you can use SQL functions as well: e.g.: `cast(TransactionTime as Date) = '2021-11-21'` - **orderBy** - define a columns to sort e.g.: `OrderDate DESC, OrderId ASC - **top**` - specify the number of records to return. -- **join** - combine rows from two or more tables, based on a related column between them. You can define array `[JoinType, TableToJoin, JoinCondition]` or: `['InnerJoin', 'Customers c', 'c.CustomerId = t.CustomerId']` +- **join** - combine rows from two or more tables, based on a related column between them. You can define array `[JoinType, TableToJoin, JoinCondition, JoinCondition2]` or: `['InnerJoin', 'Customers c', 'c.CustomerId = t.CustomerId']` ### Query Examples diff --git a/package.json b/package.json index 657fea1..4babf93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.7", + "version": "0.3.8", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/db-types.ts b/src/db-types.ts index 20ac3c7..74a9ff5 100644 --- a/src/db-types.ts +++ b/src/db-types.ts @@ -108,3 +108,18 @@ export enum PostgreSqlDataTypes { // ENUM = 'Enum', // ARRAY = 'Array' } + +export enum JoinType { + InnerJoin = "InnerJoin", + LeftJoin = "LeftJoin", + RightJoin = "RightJoin", + FullJoin = "FullJoin", +} + +export interface TableJoinDto { + tableName: string; + tableAlias?: string; + joinType: JoinType; + joinCondition: string; + joinCondition2?: string; +} diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 47d5c06..fdc5085 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -9,6 +9,7 @@ import { } from "datapipe-js"; import { dateToString, fromTable, toTable } from "datapipe-js/utils"; import { DbTypeConverter } from "./db-type-converter"; +import { JoinType, TableJoinDto } from "./db-types"; export * from "./db-types"; @@ -90,7 +91,7 @@ export interface SqlReadQueryInfo { * Examples: * - ['InnerJoin', 'Customers c', 't.CustomerId = c.CustomerID'] - inner joins mainTable to Cutomers table */ - joins?: [JoinType, string, string][]; + joins?: [JoinType, string, string, string?][]; } /** @@ -396,12 +397,13 @@ export class SqlDataApi { join( joinType: JoinType, tableName: string, - joinCondition: string + joinCondition: string, + joinCondition2?: string ): SqlDataApi { if (!this.queryInfo.joins) { this.queryInfo.joins = []; } - this.queryInfo.joins.push([joinType, tableName, joinCondition]); + this.queryInfo.joins.push([joinType, tableName, joinCondition, joinCondition2]); return this; } @@ -867,14 +869,16 @@ export class SqlDataApi { joins: TableJoinDto[], joinType: JoinType, tableOrViewWithAlias: string, - joinCondition: string + joinCondition: string, + joinCondition2?: string ): TableJoinDto[] { const nameAlias = extractNameAndAlias(tableOrViewWithAlias); joins.push({ - joinCondition, joinType, tableAlias: nameAlias.alias, tableName: nameAlias.name, + joinCondition, + joinCondition2, }); return joins; } @@ -884,7 +888,7 @@ export class SqlDataApi { if (queryInfo.joins && queryInfo.joins.length) { for (const j of queryInfo.joins) { - join(tablesJoin, j[0] as JoinType, j[1], j[2]); + join(tablesJoin, j[0] as JoinType, j[1], j[2], j[3]); } } @@ -957,17 +961,3 @@ interface RequestSqlQueryInfo { mainTableAlias?: string; tablesJoin?: TableJoinDto[]; } - -enum JoinType { - InnerJoin = "InnerJoin", - LeftJoin = "LeftJoin", - RightJoin = "RightJoin", - FullJoin = "FullJoin", -} - -interface TableJoinDto { - tableName: string; - tableAlias?: string; - joinType: JoinType; - joinCondition: string; -} From d4465f6dadd324277192d7e0280a45b8026792a4 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Fri, 6 Oct 2023 08:59:38 +0100 Subject: [PATCH 07/10] added addFilter method which appends filter condition --- package.json | 4 ++-- src/sql-data.api.ts | 28 +++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4babf93..4feba6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.8", + "version": "0.3.9", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", @@ -36,7 +36,7 @@ "homepage": "https://github.com/FalconSoft/sql-data-api-client-js", "dependencies": { "axios": "^0.21.1", - "datapipe-js": "^0.3.19" + "datapipe-js": "^0.3.29" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^4.8.2", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index fdc5085..0946873 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -361,6 +361,27 @@ export class SqlDataApi { return this; } + // fluent API methods + /** + * Append filter (with AND) and returns same + * - use this method for a fluent API + * @param filter filter string e.g. "country='UK' or city=@city" + * @param filterParams filter parameters e.g. {city: 'London'} + * @returns same SqlDataApi instance + */ + andFilter( + filter: string, + filterParams?: Record + ): SqlDataApi { + const cf = this.queryInfo.filter || ""; + this.queryInfo.filter = `${cf} ${cf ? " AND " : ""} ${filter}`.trim(); + this.queryInfo.filterParams = { + ...(this.queryInfo.filterParams || {}), + ...filterParams, + }; + return this; + } + /** * sets fields that you want to setup * - use this method for a fluent API @@ -403,7 +424,12 @@ export class SqlDataApi { if (!this.queryInfo.joins) { this.queryInfo.joins = []; } - this.queryInfo.joins.push([joinType, tableName, joinCondition, joinCondition2]); + this.queryInfo.joins.push([ + joinType, + tableName, + joinCondition, + joinCondition2, + ]); return this; } From 4c862806bd97755af86ccbb68e01698b294be7d3 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Mon, 11 Mar 2024 19:46:49 +0000 Subject: [PATCH 08/10] added cancellation functionality --- README.md | 11 ++++++ package.json | 6 +-- src/sql-data.api.ts | 95 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 88 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c247f38..866fa0a 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,17 @@ Executes `sql` script in the server and returns either raw table or array of obj ``` +## Cancellation + +You can cancel any of your http request by setting `AbortController` + +```js +setAbortController(abortController: AbortController): SqlDataApi +``` + +setAbortController is part of chaining method of sqlDataApi or you can pass it to the constructor and factory method + + ## License A permissive MIT License (c) FalconSoft Ltd. diff --git a/package.json b/package.json index 4feba6e..400f479 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.9", + "version": "0.3.10", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", @@ -35,8 +35,8 @@ }, "homepage": "https://github.com/FalconSoft/sql-data-api-client-js", "dependencies": { - "axios": "^0.21.1", - "datapipe-js": "^0.3.29" + "axios": "^1.6.7", + "datapipe-js": "^0.3.31" }, "devDependencies": { "@typescript-eslint/eslint-plugin": "^4.8.2", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 0946873..13aba8c 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -182,7 +182,8 @@ export async function authenticate( */ export function sqlDataApi( connectionName: string, - config?: { baseUrl?: string; userAccessToken?: string; bearerToken?: string } + config?: { baseUrl?: string; userAccessToken?: string; bearerToken?: string }, + abortController?: AbortController ): SqlDataApi { const cfg = { userAccessToken: config?.userAccessToken || SqlDataApi.UserAccessToken, @@ -191,7 +192,8 @@ export function sqlDataApi( return new SqlDataApi( config?.baseUrl || SqlDataApi.BaseUrl, connectionName, - cfg + cfg, + abortController ); } @@ -338,8 +340,9 @@ export class SqlDataApi { constructor( private baseUrl: string, private connectionName: string, - config: { userAccessToken?: string; bearerToken?: string } - ) { + config: { userAccessToken?: string; bearerToken?: string }, + private abortController?: AbortController + ) { this.userAccessToken = config?.userAccessToken; this.bearerToken = config?.bearerToken; } @@ -476,6 +479,11 @@ export class SqlDataApi { ); } + setAbortController(abortController: AbortController): SqlDataApi { + this.abortController = abortController; + return this; + } + /** * Updates data in the table based on filter parameters * @returns Number of rows affected @@ -506,9 +514,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const res = await httpRequest("POST", url, dto, { - headers: { ...appHttpHeaders, ...headers }, - }); + const httpConfig: AxiosRequestConfig = { + headers: Object.assign(appHttpHeaders, headers), + }; + + if (this.abortController) { + httpConfig.signal = this.abortController.signal; + } + + const res = await httpRequest("POST", url, dto, httpConfig); if (res.errorMessage) { throw new Error(res.errorMessage); @@ -543,9 +557,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const res = await httpRequest("POST", url, dto, { - headers: { ...appHttpHeaders, ...headers }, - }); + const httpConfig: AxiosRequestConfig = { + headers: Object.assign(appHttpHeaders, headers), + }; + + if (this.abortController) { + httpConfig.signal = this.abortController.signal; + } + + const res = await httpRequest("POST", url, dto, httpConfig); if (res.errorMessage) { throw new Error(res.errorMessage); @@ -622,9 +642,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const res = await httpRequest("POST", url, dto, { - headers: { ...appHttpHeaders, ...headers }, - }); + const httpConfig: AxiosRequestConfig = { + headers: Object.assign(appHttpHeaders, headers), + }; + + if (this.abortController) { + httpConfig.signal = this.abortController.signal; + } + + const res = await httpRequest("POST", url, dto, httpConfig); if (res.errorMessage) { throw new Error(res.errorMessage); @@ -672,9 +698,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const res = await httpRequest("POST", url, dto, { - headers: { ...appHttpHeaders, ...headers }, - }); + const httpConfig: AxiosRequestConfig = { + headers: Object.assign(appHttpHeaders, headers), + }; + + if (this.abortController) { + httpConfig.signal = this.abortController.signal; + } + + const res = await httpRequest("POST", url, dto, httpConfig); if (res.errorMessage) { throw new Error(res.errorMessage); @@ -733,11 +765,19 @@ export class SqlDataApi { if (!items || !items.length) { if (itemsToDelete?.length) { + const httpConfig: AxiosRequestConfig = { + headers: Object.assign(appHttpHeaders, headersValue), + }; + + if (this.abortController) { + httpConfig.signal = this.abortController.signal; + } + const res = await httpRequest( "POST", url, { itemsToDelete }, - { headers: { ...appHttpHeaders, ...headersValue } } + httpConfig ); if (res.errorMessage) { @@ -788,9 +828,16 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const res = await httpRequest("POST", url, body, { + const httpConfig: AxiosRequestConfig = { headers: Object.assign(appHttpHeaders, headersValue), - }); + }; + + if (this.abortController) { + if (this.abortController.signal?.aborted) break; + httpConfig.signal = this.abortController.signal; + } + + const res = await httpRequest("POST", url, body, httpConfig); if (res.errorMessage) { throw new Error(res.errorMessage); @@ -949,9 +996,15 @@ export class SqlDataApi { url += `?$accessToken=${this.userAccessToken}`; } - const res = await httpRequest("POST", url, request, { + const httpConfig = { headers: { ...appHttpHeaders, ...headers }, - }); + } as AxiosRequestConfig; + + if (this.abortController) { + httpConfig.signal = this.abortController.signal; + } + + const res = await httpRequest("POST", url, request, httpConfig); if (res.errorMessage) { throw new Error(res.errorMessage); From a7a6bb4d042f00054861e71baf817672b38758d6 Mon Sep 17 00:00:00 2001 From: Pavlo Date: Wed, 27 Mar 2024 21:43:13 +0000 Subject: [PATCH 09/10] added cancellation message --- package.json | 2 +- src/sql-data.api.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 400f479..6f07998 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.10", + "version": "0.3.11", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 13aba8c..320c21b 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -1,4 +1,4 @@ -import axios, { AxiosRequestConfig } from "axios"; +import axios, { AxiosRequestConfig, isCancel } from "axios"; import { PrimitivesObject, PrimitiveType, @@ -236,10 +236,15 @@ export function httpRequest( response.data || response.statusText || "Http Connection Error"; + if (typeof errorMessage === "object") { errorMessage = JSON.stringify(errorMessage); } + if (isCancel(e)) { + errorMessage = "Request cancelled"; + } + return { isOk: false, status: response.status, @@ -342,7 +347,7 @@ export class SqlDataApi { private connectionName: string, config: { userAccessToken?: string; bearerToken?: string }, private abortController?: AbortController - ) { + ) { this.userAccessToken = config?.userAccessToken; this.bearerToken = config?.bearerToken; } From bd8a9fce0c7ca987d13ed698263e34a7e7a3855c Mon Sep 17 00:00:00 2001 From: Pavlo Date: Thu, 28 Mar 2024 08:12:15 +0000 Subject: [PATCH 10/10] changed Abort Controller to Abort signal --- package.json | 2 +- src/sql-data.api.ts | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 6f07998..6a26ea3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sql-data-api", - "version": "0.3.11", + "version": "0.3.12", "description": "Sql Data Api client for Javascript", "keywords": [ "sql", diff --git a/src/sql-data.api.ts b/src/sql-data.api.ts index 320c21b..93ac37c 100644 --- a/src/sql-data.api.ts +++ b/src/sql-data.api.ts @@ -183,7 +183,7 @@ export async function authenticate( export function sqlDataApi( connectionName: string, config?: { baseUrl?: string; userAccessToken?: string; bearerToken?: string }, - abortController?: AbortController + abortSignal?: AbortSignal ): SqlDataApi { const cfg = { userAccessToken: config?.userAccessToken || SqlDataApi.UserAccessToken, @@ -193,7 +193,7 @@ export function sqlDataApi( config?.baseUrl || SqlDataApi.BaseUrl, connectionName, cfg, - abortController + abortSignal ); } @@ -346,7 +346,7 @@ export class SqlDataApi { private baseUrl: string, private connectionName: string, config: { userAccessToken?: string; bearerToken?: string }, - private abortController?: AbortController + private abortSignal?: AbortSignal ) { this.userAccessToken = config?.userAccessToken; this.bearerToken = config?.bearerToken; @@ -484,8 +484,8 @@ export class SqlDataApi { ); } - setAbortController(abortController: AbortController): SqlDataApi { - this.abortController = abortController; + setAbortSignal(signal: AbortSignal): SqlDataApi { + this.abortSignal = signal; return this; } @@ -523,8 +523,8 @@ export class SqlDataApi { headers: Object.assign(appHttpHeaders, headers), }; - if (this.abortController) { - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + httpConfig.signal = this.abortSignal; } const res = await httpRequest("POST", url, dto, httpConfig); @@ -566,8 +566,8 @@ export class SqlDataApi { headers: Object.assign(appHttpHeaders, headers), }; - if (this.abortController) { - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + httpConfig.signal = this.abortSignal; } const res = await httpRequest("POST", url, dto, httpConfig); @@ -651,8 +651,8 @@ export class SqlDataApi { headers: Object.assign(appHttpHeaders, headers), }; - if (this.abortController) { - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + httpConfig.signal = this.abortSignal; } const res = await httpRequest("POST", url, dto, httpConfig); @@ -707,8 +707,8 @@ export class SqlDataApi { headers: Object.assign(appHttpHeaders, headers), }; - if (this.abortController) { - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + httpConfig.signal = this.abortSignal; } const res = await httpRequest("POST", url, dto, httpConfig); @@ -774,8 +774,8 @@ export class SqlDataApi { headers: Object.assign(appHttpHeaders, headersValue), }; - if (this.abortController) { - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + httpConfig.signal = this.abortSignal; } const res = await httpRequest( @@ -837,9 +837,9 @@ export class SqlDataApi { headers: Object.assign(appHttpHeaders, headersValue), }; - if (this.abortController) { - if (this.abortController.signal?.aborted) break; - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + if (this.abortSignal?.aborted) break; + httpConfig.signal = this.abortSignal; } const res = await httpRequest("POST", url, body, httpConfig); @@ -1005,8 +1005,8 @@ export class SqlDataApi { headers: { ...appHttpHeaders, ...headers }, } as AxiosRequestConfig; - if (this.abortController) { - httpConfig.signal = this.abortController.signal; + if (this.abortSignal) { + httpConfig.signal = this.abortSignal; } const res = await httpRequest("POST", url, request, httpConfig);