From a93a532b2f04ffd856fed1580dd17da89273e76c Mon Sep 17 00:00:00 2001 From: yunchengyang Date: Sun, 3 Dec 2023 23:12:57 +1100 Subject: [PATCH 1/6] added csv and json output for run query on instnace --- src/stackql.test.ts | 49 ++++++++++++++++++++++++++++++++++++++++++++- src/stackql.ts | 47 +++++++++++++++++++++++++++++++++++++++++-- testing/utils.ts | 19 ++++++++++++++++++ 3 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/stackql.test.ts b/src/stackql.test.ts index 4462c1c..ae426d6 100644 --- a/src/stackql.test.ts +++ b/src/stackql.test.ts @@ -1,6 +1,10 @@ import { assertStringIncludes } from "https://deno.land/std@0.206.0/assert/mod.ts"; import { StackQL } from "./stackql.ts"; -import { startStackQLServer } from "../testing/utils.ts"; +import { + isCsvString, + isJsonString, + startStackQLServer, +} from "../testing/utils.ts"; import { assertEquals, assertExists, @@ -117,6 +121,49 @@ Deno.test("Set proxy properties from configs", async () => { runCommandSpy.restore(); }); +Deno.test("run query Output: json", async () => { + await setupStackQL(); + const stackQL = new StackQL(); + await stackQL.initialize({ + serverMode: false, + outputFormat: "json", + }); + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'`; + + const result = await stackQL.runQuery(githubTestQuery); + + const params = stackQL.getParams(); + + assertEquals(params, [ + "--output", + "json", + ]); + assert(isJsonString(result)); + assert(!(await isCsvString(result))); +}); + +Deno.test("run query Output: csv", async () => { + await setupStackQL(); + const stackQL = new StackQL(); + await stackQL.initialize({ + serverMode: false, + outputFormat: "csv", + }); + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'`; + + const result = await stackQL.runQuery(githubTestQuery); + const params = stackQL.getParams(); + + assertEquals(params, [ + "--output", + "csv", + ]); + assert(!isJsonString(result)); + assert(await isCsvString(result)); +}); + Deno.test("StackQL runServerQuery", async () => { const { closeProcess } = await startStackQLServer(); const stackQL = new StackQL(); diff --git a/src/stackql.ts b/src/stackql.ts index a7ae4eb..353aa8c 100644 --- a/src/stackql.ts +++ b/src/stackql.ts @@ -17,6 +17,7 @@ export interface StackQLConfig { proxyUser?: string; proxyPassword?: string; proxyScheme?: "http" | "https"; + outputFormat?: "csv" | "json"; } export class StackQL { @@ -24,7 +25,7 @@ export class StackQL { private downloader: Downloader = new Downloader(); private serverMode = false; private connection?: Client; - private format: "object" = "object"; + private outputFormat: "csv" | "json" = "json"; private params: string[] = []; private version: string | undefined; // The version number of the `stackql` executable (not supported in `server_mode`) private sha: string | undefined; // The commit (short) sha for the installed `stackql` binary build (not supported in `server_mode`). @@ -47,6 +48,13 @@ export class StackQL { return { version: this.version, sha: this.sha }; } + async execute(query: string) { + if (this.serverMode) { + const result = await this.runServerQuery(query); + } + return await this.runQuery(query); + } + private async updateVersion() { if (!this.binaryPath) { throw new Error("Binary path not found"); @@ -61,12 +69,22 @@ export class StackQL { this.sha = sha; } } + + /** + * Upgrade the `stackql` executable to the latest version + * @returns The version number of the `stackql` executable (not supported in `server_mode`) + */ async upgrade() { this.binaryPath = await this.downloader.upgradeStackQL(); await this.updateVersion(); return this.getVersion(); } + /** + * Initialize the `stackql` executable + * @param config The configuration object + * @returns The binary path of the `stackql` executable if `server_mode` is `false`, otherwise `undefined` + */ public async initialize(config: StackQLConfig) { this.binaryPath = config.binaryPath; this.serverMode = config.serverMode || false; @@ -80,6 +98,7 @@ export class StackQL { this.binaryPath = await this.downloader.setupStackQL(); this.setProperties(config); } + private binaryExist() { return !!this.binaryPath && osUtils.fileExists(this.binaryPath); } @@ -139,8 +158,24 @@ export class StackQL { if (config.proxyHost !== undefined) { this.setProxyProperties(config); } + + if (config.outputFormat !== undefined) { + if (!["csv", "json"].includes(config.outputFormat)) { + throw new Error( + `Invalid outputFormat. Expected one of ['csv', 'json'], got ${config.outputFormat}.`, + ); + } + this.params.push("--output"); + this.params.push(config.outputFormat); + this.outputFormat = config.outputFormat; + } } + /** + * Run a StackQL query + * @param query The StackQL query + * @returns The result of the query + */ public async runQuery(query: string) { assertExists(this.binaryPath); const args = ["exec", query].concat(this.params); @@ -165,15 +200,23 @@ export class StackQL { this.connection = await server.connect(connectionString); } + /** + * Close the connection to the stackql server + */ public async closeConnection() { if (this.connection) { await this.connection.end(); } } + /** + * Run a StackQL query on the server + * @param query The StackQL query + * @returns The result of the query + */ public async runServerQuery(query: string) { try { - if (this.format === "object") { + if (this.outputFormat === "json") { const result = await this.queryObjectFormat(query); return result; } diff --git a/testing/utils.ts b/testing/utils.ts index 88f8463..02f98b0 100644 --- a/testing/utils.ts +++ b/testing/utils.ts @@ -1,5 +1,6 @@ import { existsSync } from "https://deno.land/std/fs/mod.ts"; import { join } from "https://deno.land/std@0.133.0/path/mod.ts"; +import { parse } from "https://deno.land/std@0.134.0/encoding/csv.ts"; export const removeStackQLDownload = async () => { const projectDir = Deno.cwd(); @@ -42,3 +43,21 @@ export const startStackQLServer = async (port = 5444) => { }; return { closeProcess }; }; + +export const isJsonString = (str: string) => { + try { + JSON.parse(str); + } catch (_error) { + return false; + } + return true; +}; + +export const isCsvString = async (str: string) => { + try { + await parse(str); + } catch (_error) { + return false; + } + return true; +}; From 6ac3188c9664e4789fcb72a1edde5d19a5729262 Mon Sep 17 00:00:00 2001 From: Yuncheng Date: Mon, 4 Dec 2023 08:56:22 +1100 Subject: [PATCH 2/6] added new deno task runner for test --- README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0198492..83db40c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # stackqljs -[__StackQL__](https://github.com/stackql/stackql) client library for Deno and Node.js that exposes all features StackQL. +[**StackQL**](https://github.com/stackql/stackql) client library for Deno and Node.js that exposes all features StackQL. ## Requirements @@ -14,15 +14,18 @@ - No dependencies - Works in server mode (as a pg wire protocol server client) as well as local mode where it is a wrapper for the `stackql` binary for the target platform - Exposes methods analagous to [`pystackql`](https://pystackql.readthedocs.io/en/latest/), including: - - `connect` - connect to a StackQL server - if in server mode - - `upgrade` - if in local mode, upgrade the local stackql binary from the latest release - - `execute` - execute a query returns an array of rows (objects) - - `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`) - - `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema + - `connect` - connect to a StackQL server - if in server mode + - `upgrade` - if in local mode, upgrade the local stackql binary from the latest release + - `execute` - execute a query returns an array of rows (objects) + - `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`) + - `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema ## Test + ### Requirement + - To run the tests locally, [install StackQL](https://stackql.io/docs/installing-stackql) first. + +``` +deno task test ``` - deno test --allow-net --allow-read --allow-write --allow-env --allow-run -``` \ No newline at end of file From 9574a593f45d3148f403ca18dbd5ddf95124b3f7 Mon Sep 17 00:00:00 2001 From: Yuncheng Date: Mon, 4 Dec 2023 08:56:27 +1100 Subject: [PATCH 3/6] added new deno task runner for test --- deno.json | 5 ++ deno.lock | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 deno.json create mode 100644 deno.lock diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..ae9616d --- /dev/null +++ b/deno.json @@ -0,0 +1,5 @@ +{ + "tasks": { + "test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-run" + } +} diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..937c26b --- /dev/null +++ b/deno.lock @@ -0,0 +1,167 @@ +{ + "version": "2", + "remote": { + "https://deno.land/std@0.133.0/_deno_unstable.ts": "23a1a36928f1b6d3b0170aaa67de09af12aa998525f608ff7331b9fb364cbde6", + "https://deno.land/std@0.133.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.133.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", + "https://deno.land/std@0.133.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", + "https://deno.land/std@0.133.0/fs/copy.ts": "9248d1492599957af8c693ceb10a432b09f0b0b61c60a4d6aff29b0c7d3a17b3", + "https://deno.land/std@0.133.0/fs/empty_dir.ts": "7274d87160de34cbed0531e284df383045cf43543bbeadeb97feac598bd8f3c5", + "https://deno.land/std@0.133.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", + "https://deno.land/std@0.133.0/fs/ensure_file.ts": "7d353e64fee3d4d1e7c6b6726a2a5e987ba402c15fb49566309042887349c545", + "https://deno.land/std@0.133.0/fs/ensure_link.ts": "489e23df9fe3e6636048b5830ddf0f111eb29621eb85719255ad9bd645f3471b", + "https://deno.land/std@0.133.0/fs/ensure_symlink.ts": "88dc83de1bc90ed883dd458c2d2eae3d5834a4617d12925734836e1f0803b274", + "https://deno.land/std@0.133.0/fs/eol.ts": "b92f0b88036de507e7e6fbedbe8f666835ea9dcbf5ac85917fa1fadc919f83a5", + "https://deno.land/std@0.133.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d", + "https://deno.land/std@0.133.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3", + "https://deno.land/std@0.133.0/fs/mod.ts": "4dc052c461c171abb5c25f6e0f218ab838a716230930b534ba351745864b7d6d", + "https://deno.land/std@0.133.0/fs/move.ts": "0573cedcf583f09a9494f2dfccbf67de68a93629942d6b5e6e74a9e45d4e8a2e", + "https://deno.land/std@0.133.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f", + "https://deno.land/std@0.133.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", + "https://deno.land/std@0.133.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", + "https://deno.land/std@0.133.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", + "https://deno.land/std@0.133.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", + "https://deno.land/std@0.133.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", + "https://deno.land/std@0.133.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", + "https://deno.land/std@0.133.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", + "https://deno.land/std@0.133.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", + "https://deno.land/std@0.133.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", + "https://deno.land/std@0.160.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.160.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", + "https://deno.land/std@0.160.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06", + "https://deno.land/std@0.160.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0", + "https://deno.land/std@0.160.0/async/debounce.ts": "dc8b92d4a4fe7eac32c924f2b8d3e62112530db70cadce27042689d82970b350", + "https://deno.land/std@0.160.0/async/deferred.ts": "d8fb253ffde2a056e4889ef7e90f3928f28be9f9294b6505773d33f136aab4e6", + "https://deno.land/std@0.160.0/async/delay.ts": "0419dfc993752849692d1f9647edf13407c7facc3509b099381be99ffbc9d699", + "https://deno.land/std@0.160.0/async/mod.ts": "dd0a8ed4f3984ffabe2fcca7c9f466b7932d57b1864ffee148a5d5388316db6b", + "https://deno.land/std@0.160.0/async/mux_async_iterator.ts": "3447b28a2a582224a3d4d3596bccbba6e85040da3b97ed64012f7decce98d093", + "https://deno.land/std@0.160.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239", + "https://deno.land/std@0.160.0/async/tee.ts": "9af3a3e7612af75861308b52249e167f5ebc3dcfc8a1a4d45462d96606ee2b70", + "https://deno.land/std@0.160.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4", + "https://deno.land/std@0.160.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a", + "https://deno.land/std@0.160.0/bytes/mod.ts": "b2e342fd3669176a27a4e15061e9d588b89c1aaf5008ab71766e23669565d179", + "https://deno.land/std@0.160.0/crypto/_fnv/fnv32.ts": "aa9bddead8c6345087d3abd4ef35fb9655622afc333fc41fff382b36e64280b5", + "https://deno.land/std@0.160.0/crypto/_fnv/fnv64.ts": "625d7e7505b6cb2e9801b5fd6ed0a89256bac12b2bbb3e4664b85a88b0ec5bef", + "https://deno.land/std@0.160.0/crypto/_fnv/index.ts": "a8f6a361b4c6d54e5e89c16098f99b6962a1dd6ad1307dbc97fa1ecac5d7060a", + "https://deno.land/std@0.160.0/crypto/_fnv/util.ts": "4848313bed7f00f55be3cb080aa0583fc007812ba965b03e4009665bde614ce3", + "https://deno.land/std@0.160.0/crypto/_wasm_crypto/lib/deno_std_wasm_crypto.generated.mjs": "258b484c2da27578bec61c01d4b62c21f72268d928d03c968c4eb590cb3bd830", + "https://deno.land/std@0.160.0/crypto/_wasm_crypto/mod.ts": "6c60d332716147ded0eece0861780678d51b560f533b27db2e15c64a4ef83665", + "https://deno.land/std@0.160.0/crypto/keystack.ts": "e481eed28007395e554a435e880fee83a5c73b9259ed8a135a75e4b1e4f381f7", + "https://deno.land/std@0.160.0/crypto/mod.ts": "fadedc013b4a86fda6305f1adc6d1c02225834d53cff5d95cc05f62b25127517", + "https://deno.land/std@0.160.0/crypto/timing_safe_equal.ts": "82a29b737bc8932d75d7a20c404136089d5d23629e94ba14efa98a8cc066c73e", + "https://deno.land/std@0.160.0/datetime/formatter.ts": "7c8e6d16a0950f400aef41b9f1eb9168249869776ec520265dfda785d746589e", + "https://deno.land/std@0.160.0/datetime/mod.ts": "ea927ca96dfb28c7b9a5eed5bdc7ac46bb9db38038c4922631895cea342fea87", + "https://deno.land/std@0.160.0/datetime/tokenizer.ts": "7381e28f6ab51cb504c7e132be31773d73ef2f3e1e50a812736962b9df1e8c47", + "https://deno.land/std@0.160.0/encoding/base64.ts": "c57868ca7fa2fbe919f57f88a623ad34e3d970d675bdc1ff3a9d02bba7409db2", + "https://deno.land/std@0.160.0/encoding/base64url.ts": "a5f82a9fa703bd85a5eb8e7c1296bc6529e601ebd9642cc2b5eaa6b38fa9e05a", + "https://deno.land/std@0.160.0/encoding/hex.ts": "4cc5324417cbb4ac9b828453d35aed45b9cc29506fad658f1f138d981ae33795", + "https://deno.land/std@0.160.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", + "https://deno.land/std@0.160.0/io/buffer.ts": "fae02290f52301c4e0188670e730cd902f9307fb732d79c4aa14ebdc82497289", + "https://deno.land/std@0.160.0/io/types.d.ts": "107e1e64834c5ba917c783f446b407d33432c5d612c4b3430df64fc2b4ecf091", + "https://deno.land/std@0.160.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", + "https://deno.land/std@0.160.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", + "https://deno.land/std@0.160.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", + "https://deno.land/std@0.160.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", + "https://deno.land/std@0.160.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", + "https://deno.land/std@0.160.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac", + "https://deno.land/std@0.160.0/path/posix.ts": "6b63de7097e68c8663c84ccedc0fd977656eb134432d818ecd3a4e122638ac24", + "https://deno.land/std@0.160.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", + "https://deno.land/std@0.160.0/path/win32.ts": "ee8826dce087d31c5c81cd414714e677eb68febc40308de87a2ce4b40e10fb8d", + "https://deno.land/std@0.160.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", + "https://deno.land/std@0.160.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", + "https://deno.land/std@0.160.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8", + "https://deno.land/std@0.188.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.188.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.188.0/fs/_util.ts": "579038bebc3bd35c43a6a7766f7d91fbacdf44bc03468e9d3134297bb99ed4f9", + "https://deno.land/std@0.188.0/fs/copy.ts": "14214efd94fc3aa6db1e4af2b4b9578e50f7362b7f3725d5a14ad259a5df26c8", + "https://deno.land/std@0.188.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", + "https://deno.land/std@0.188.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.188.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", + "https://deno.land/std@0.188.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", + "https://deno.land/std@0.188.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1", + "https://deno.land/std@0.188.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", + "https://deno.land/std@0.188.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", + "https://deno.land/std@0.188.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", + "https://deno.land/std@0.188.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", + "https://deno.land/std@0.188.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", + "https://deno.land/std@0.188.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", + "https://deno.land/std@0.188.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.188.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.188.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", + "https://deno.land/std@0.188.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.188.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.188.0/path/mod.ts": "ee161baec5ded6510ee1d1fb6a75a0f5e4b41f3f3301c92c716ecbdf7dae910d", + "https://deno.land/std@0.188.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", + "https://deno.land/std@0.188.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.188.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", + "https://deno.land/std@0.206.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.206.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", + "https://deno.land/std@0.206.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.206.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.206.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.206.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.206.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.206.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.206.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", + "https://deno.land/std@0.206.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.206.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.206.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.206.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.206.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.206.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.206.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.206.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.206.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.206.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.206.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.206.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.206.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.206.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.206.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.206.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.206.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.206.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.206.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.206.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.206.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.206.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.206.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", + "https://deno.land/std@0.207.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.207.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", + "https://deno.land/std@0.207.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.207.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.207.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.207.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.207.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.207.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.207.0/fmt/colors.ts": "34b3f77432925eb72cf0bfb351616949746768620b8e5ead66da532f93d10ba2", + "https://deno.land/std@0.207.0/testing/mock.ts": "5c4363d6846179390c1ef4347ea0fddfd9780705089228cba331dc49c2a4f3c1", + "https://deno.land/x/postgres@v0.17.0/client.ts": "348779c9f6a1c75ef1336db662faf08dce7d2101ff72f0d1e341ba1505c8431d", + "https://deno.land/x/postgres@v0.17.0/client/error.ts": "0817583b666fd546664ed52c1d37beccc5a9eebcc6e3c2ead20ada99b681e5f7", + "https://deno.land/x/postgres@v0.17.0/connection/auth.ts": "1070125e2ac4ca4ade36d69a4222d37001903092826d313217987583edd61ce9", + "https://deno.land/x/postgres@v0.17.0/connection/connection.ts": "428ed3efa055870db505092b5d3545ef743497b7b4b72cf8f0593e7dd4788acd", + "https://deno.land/x/postgres@v0.17.0/connection/connection_params.ts": "52bfe90e8860f584b95b1b08c254dde97c3aa763c4b6bee0c80c5930e35459e0", + "https://deno.land/x/postgres@v0.17.0/connection/message.ts": "f9257948b7f87d58bfbfe3fc6e2e08f0de3ef885655904d56a5f73655cc22c5a", + "https://deno.land/x/postgres@v0.17.0/connection/message_code.ts": "466719008b298770c366c5c63f6cf8285b7f76514dadb4b11e7d9756a8a1ddbf", + "https://deno.land/x/postgres@v0.17.0/connection/packet.ts": "050aeff1fc13c9349e89451a155ffcd0b1343dc313a51f84439e3e45f64b56c8", + "https://deno.land/x/postgres@v0.17.0/connection/scram.ts": "0c7a2551fe7b1a1c62dd856b7714731a7e7534ccca10093336782d1bfc5b2bd2", + "https://deno.land/x/postgres@v0.17.0/deps.ts": "f47ccb41f7f97eaad455d94f407ef97146ae99443dbe782894422c869fbba69e", + "https://deno.land/x/postgres@v0.17.0/mod.ts": "a1e18fd9e6fedc8bc24e5aeec3ae6de45e2274be1411fb66e9081420c5e81d7d", + "https://deno.land/x/postgres@v0.17.0/pool.ts": "892db7b5e1787988babecc994a151ebbd7d017f080905cbe9c3d7b44a73032a9", + "https://deno.land/x/postgres@v0.17.0/query/array_parser.ts": "f8a229d82c3801de8266fa2cc4afe12e94fef8d0c479e73655c86ed3667ef33f", + "https://deno.land/x/postgres@v0.17.0/query/decode.ts": "44a4a6cbcf494ed91a4fecae38a57dce63a7b519166f02c702791d9717371419", + "https://deno.land/x/postgres@v0.17.0/query/decoders.ts": "16cb0e60227d86692931e315421b15768c78526e3aeb84e25fcc4111096de9fd", + "https://deno.land/x/postgres@v0.17.0/query/encode.ts": "5f1418a2932b7c2231556e4a5f5f56efef48728014070cfafe7656963f342933", + "https://deno.land/x/postgres@v0.17.0/query/oid.ts": "8c33e1325f34e4ca9f11a48b8066c8cfcace5f64bc1eb17ad7247af4936999e1", + "https://deno.land/x/postgres@v0.17.0/query/query.ts": "edb473cbcfeff2ee1c631272afb25d079d06b66b5853f42492725b03ffa742b6", + "https://deno.land/x/postgres@v0.17.0/query/transaction.ts": "8e75c3ce0aca97da7fe126e68f8e6c08d640e5c8d2016e62cee5c254bebe7fe8", + "https://deno.land/x/postgres@v0.17.0/query/types.ts": "a6dc8024867fe7ccb0ba4b4fa403ee5d474c7742174128c8e689c3b5e5eaa933", + "https://deno.land/x/postgres@v0.17.0/utils/deferred.ts": "dd94f2a57355355c47812b061a51b55263f72d24e9cb3fdb474c7519f4d61083", + "https://deno.land/x/postgres@v0.17.0/utils/utils.ts": "19c3527ddd5c6c4c49ae36397120274c7f41f9d3cbf479cb36065d23329e9f90", + "https://deno.land/x/zip@v1.2.5/compress.ts": "43d9f4440960d15a85aec58f5d365acc25530d3d4186b2f5f896c090ecac20e8", + "https://deno.land/x/zip@v1.2.5/decompress.ts": "0bce3d453726f686274fab3f6c19b72b5e74223a00d89c176b1de49a5dd5528d", + "https://deno.land/x/zip@v1.2.5/deps.ts": "79548387594b3ae1efaaa870b5a507c4d6bedede13dbd5d4ad42f6cda0aeef86", + "https://deno.land/x/zip@v1.2.5/mod.ts": "28eecbc3e1e5adf564f4aa465e64268713a05653104bacdcb04561533f8caf57", + "https://deno.land/x/zip@v1.2.5/utils.ts": "43c323f2b79f9db1976c5739bbb1f9cced20e8077ca7e7e703f9d01d4330bd9d" + } +} From ab25c55c297749a992f8698a0deca7a290afb205 Mon Sep 17 00:00:00 2001 From: Yuncheng Date: Mon, 4 Dec 2023 23:23:08 +1100 Subject: [PATCH 4/6] wip: debugging failed test --- deno.lock | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/deno.lock b/deno.lock index 937c26b..9f2461c 100644 --- a/deno.lock +++ b/deno.lock @@ -26,6 +26,17 @@ "https://deno.land/std@0.133.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", "https://deno.land/std@0.133.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", "https://deno.land/std@0.133.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", + "https://deno.land/std@0.134.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", + "https://deno.land/std@0.134.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", + "https://deno.land/std@0.134.0/bytes/equals.ts": "a60ef9f01fb6e06a4e0343fc44d53d39d12dd66bc3f09ac8e6eb9cc1a6335e48", + "https://deno.land/std@0.134.0/bytes/mod.ts": "4cef6fe8f0de217b9babbcbb0a566402b667f18a8e6d094a45e5fb3fc1afff70", + "https://deno.land/std@0.134.0/encoding/csv.ts": "5c7c20114cd4ca93d43226350f116e3261459ff288d232663c0adb4569eae95d", + "https://deno.land/std@0.134.0/encoding/csv/_io.ts": "91a61ee8ec8151126479453c71b7cb789fbb2bd224052360e39a79f81df1f3dc", + "https://deno.land/std@0.134.0/encoding/csv_stringify.ts": "b6397d6ab330b9954872558ac21a384376c2c4f29b86fbf56b9cdd7b462fa5dd", + "https://deno.land/std@0.134.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", + "https://deno.land/std@0.134.0/io/readers.ts": "679471f3b9929b54393c9cd75b6bd178b4bc6d9aab5c0f1f9538f862cf4746fe", + "https://deno.land/std@0.134.0/io/types.d.ts": "01f60ae7ec02675b5dbed150d258fc184a78dfe5c209ef53ba4422b46b58822c", + "https://deno.land/std@0.134.0/textproto/mod.ts": "b78acf898a4e1f35eac9a955e07c84c70824662d918df988b22c0abcf2a9a987", "https://deno.land/std@0.160.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", "https://deno.land/std@0.160.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", "https://deno.land/std@0.160.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06", From cf11480d981a2370e4bf0f08f976ae2ff86a5bf9 Mon Sep 17 00:00:00 2001 From: Yuncheng Date: Sat, 9 Dec 2023 14:49:29 +1100 Subject: [PATCH 5/6] added editor format on save --- .vscode/settings.json | 10 +- deno.json | 19 ++ src/services/downloader.test.ts | 82 +++--- src/services/downloader.ts | 372 ++++++++++++------------ src/services/server.test.ts | 26 +- src/services/server.ts | 102 +++---- src/services/unpacker.ts | 60 ++-- src/stackql.test.ts | 499 ++++++++++++++++---------------- src/stackql.ts | 447 ++++++++++++++-------------- src/types/platforms.ts | 8 +- src/utils/os.test.ts | 42 +-- src/utils/os.ts | 76 ++--- 12 files changed, 883 insertions(+), 860 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c7ddc5..22ae4cf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { - "deno.enable": true, - "deno.lint": true, - "deno.unstable": true -} \ No newline at end of file + "deno.enable": true, + "deno.lint": true, + "deno.unstable": true, + "editor.formatOnSave": true, + "editor.defaultFormatter": "denoland.vscode-deno" +} diff --git a/deno.json b/deno.json index ae9616d..cdafc9b 100644 --- a/deno.json +++ b/deno.json @@ -1,4 +1,23 @@ { + "lint": { + "include": ["src/"], + "exclude": ["src/testing/", "src/**/*.test.ts", "src/*test.ts/"], + "rules": { + "tags": ["recommended"], + "include": [], + "exclude": ["no-unused-vars", "ban-untagged-todo"] + } + }, + "fmt": { + "useTabs": true, + "lineWidth": 80, + "indentWidth": 4, + "semiColons": false, + "singleQuote": true, + "proseWrap": "preserve", + "include": ["src/"], + "exclude": ["src/testdata/", "data/fixtures/**/*.ts"] + }, "tasks": { "test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-run" } diff --git a/src/services/downloader.test.ts b/src/services/downloader.test.ts index fc11f62..4295f2b 100644 --- a/src/services/downloader.test.ts +++ b/src/services/downloader.test.ts @@ -1,49 +1,49 @@ -import { assertExists } from "https://deno.land/std@0.206.0/assert/assert_exists.ts"; -import { Downloader } from "./downloader.ts"; -import { removeStackQLDownload } from "../../testing/utils.ts"; +import { assertExists } from 'https://deno.land/std@0.206.0/assert/assert_exists.ts' +import { Downloader } from './downloader.ts' +import { removeStackQLDownload } from '../../testing/utils.ts' import { - assertSpyCalls, - spy, -} from "https://deno.land/std@0.207.0/testing/mock.ts"; + assertSpyCalls, + spy, +} from 'https://deno.land/std@0.207.0/testing/mock.ts' -Deno.test("Downloader setupStackQL and upgrade Test", async () => { - // Arrange - await removeStackQLDownload(); - const downloader = new Downloader(); - let binaryPath: string; - const denoOpenSpy = spy(Deno, "open"); +Deno.test('Downloader setupStackQL and upgrade Test', async () => { + // Arrange + await removeStackQLDownload() + const downloader = new Downloader() + let binaryPath: string + const denoOpenSpy = spy(Deno, 'open') - // Act - const setupTest = async () => { - try { - binaryPath = await downloader.setupStackQL(); + // Act + const setupTest = async () => { + try { + binaryPath = await downloader.setupStackQL() - // Assert - assertExists(binaryPath); - assertSpyCalls(denoOpenSpy, 1); - // Check if the binary exists after setupStackQL is called + // Assert + assertExists(binaryPath) + assertSpyCalls(denoOpenSpy, 1) + // Check if the binary exists after setupStackQL is called - console.log( - "Test passed: setupStackQL completed without errors and binary exists." - ); - } catch (error) { - console.error("Test failed:", error); - throw error; // This will cause the test to fail - } - }; + console.log( + 'Test passed: setupStackQL completed without errors and binary exists.', + ) + } catch (error) { + console.error('Test failed:', error) + throw error // This will cause the test to fail + } + } - const upgradeTest = async () => { - try { - binaryPath = await downloader.upgradeStackQL(); + const upgradeTest = async () => { + try { + binaryPath = await downloader.upgradeStackQL() - assertExists(binaryPath); - assertSpyCalls(denoOpenSpy, 2); - } catch (error) { - console.error("Test failed:", error); - throw error; // This will cause the test to fail - } - }; + assertExists(binaryPath) + assertSpyCalls(denoOpenSpy, 2) + } catch (error) { + console.error('Test failed:', error) + throw error // This will cause the test to fail + } + } - await setupTest(); - await upgradeTest(); -}); + await setupTest() + await upgradeTest() +}) diff --git a/src/services/downloader.ts b/src/services/downloader.ts index 0e0246c..85ae7e5 100644 --- a/src/services/downloader.ts +++ b/src/services/downloader.ts @@ -1,189 +1,189 @@ -import { join } from "https://deno.land/std@0.133.0/path/mod.ts"; -import { SupportedOs } from "../types/platforms.ts"; -import { darwinUnpack, unzip } from "./unpacker.ts"; -import osUtils from "../utils/os.ts"; +import { join } from 'https://deno.land/std@0.133.0/path/mod.ts' +import { SupportedOs } from '../types/platforms.ts' +import { darwinUnpack, unzip } from './unpacker.ts' +import osUtils from '../utils/os.ts' export class Downloader { - private os: string; - private arch: string; - private urlMap: Record; - constructor() { - this.os = Deno.build.os; // 'linux', 'darwin', or 'windows' - this.arch = Deno.build.arch; // 'x86_64', 'arm64', etc. - - this.urlMap = { - [SupportedOs.Linux]: - "https://releases.stackql.io/stackql/latest/stackql_linux_amd64.zip", - [SupportedOs.Windows]: - "https://releases.stackql.io/stackql/latest/stackql_windows_amd64.zip", - [SupportedOs.Darwin]: - "https://storage.googleapis.com/stackql-public-releases/latest/stackql_darwin_multiarch.pkg", - // Additional OS-architecture combinations can be added here - }; - } - - private async downloadFile(url: string, downloadDir: string) { - const res = await fetch(url); - // create dir if not exists - - const file = await Deno.open(downloadDir, { create: true, write: true }); - - try { - await res.body?.pipeTo(file.writable).finally( - () => file.close() //TODO: fix bad resource id when closing file - ); - } catch (error) { - console.error(`ERROR: [downloadFile] ${error.message}`); - } - - console.log("Closed file"); - } - - private getUrl(): string { - const key = `${this.os}`; - const url = this.urlMap[key]; - - if (!url) { - throw new Error(`Unsupported OS type: ${this.os}`); - } - - return url; - } - - /** - * Gets binary name - * @returns binrary name - */ - private getBinaryName() { - const binaryMap: Record = { - [SupportedOs.Windows]: "stackql.exe", - [SupportedOs.Darwin]: "stackql/Payload/stackql", - [SupportedOs.Linux]: "stackql", // Default case for Linux and other platforms - }; - const os = Deno.build.os.toLowerCase(); - - if (!Object.values(SupportedOs).includes(os as SupportedOs)) { - throw new Error(`Unsupported OS type: ${os}`); - } - - const binaryOs = os as SupportedOs; - return binaryMap[binaryOs]; - } - - private async createDownloadDir(downloadDir: string) { - try { - const stat = await Deno.stat(downloadDir); - if (!stat.isDirectory) { - await Deno.mkdir(downloadDir, { recursive: true }); - } - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - await Deno.mkdir(downloadDir, { recursive: true }); - } else { - throw error; - } - } - } - /** - * Gets download dir - * @returns download dir - */ - private getDownloadDir(): string { - const projectDir = Deno.cwd(); - - if (!projectDir) { - throw new Error("Unable to determine the project directory."); - } - - const downloadDir = join(projectDir, ".stackql"); - - return downloadDir; - } - - private binaryExists(binaryName: string, downloadDir: string): boolean { - const binPath = join(downloadDir, binaryName); - try { - Deno.statSync(binPath); - return true; - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - return false; - } - throw error; - } - } - private async installStackQL(downloadDir: string) { - const url = this.getUrl(); - - const archiveFileName = `${downloadDir}/${url.split("/").pop()}`; - await this.downloadFile(url, archiveFileName); - - console.log("Unpacking stackql binary"); - const unpacker = Deno.build.os === "darwin" ? darwinUnpack : unzip; - await unpacker({ downloadDir, archiveFileName }); - } - - private async setExecutable(binaryPath: string) { - const allowExecOctal = 0o755; - await osUtils.chomod(binaryPath, allowExecOctal); - } - - private async downloadAndInstallStackQL({ - downloadDir, - binaryName, - }: { - downloadDir: string; - binaryName: string; - }) { - const binaryPath = join(downloadDir, binaryName); - await this.installStackQL(downloadDir); - await this.setExecutable(binaryPath); - return binaryPath; - } - /** - * Setup stackql binary, check if binary exists, if not download it - */ - public async setupStackQL() { - try { - const binaryName = this.getBinaryName(); - const downloadDir = this.getDownloadDir(); - await this.createDownloadDir(downloadDir); - - let binaryPath = join(downloadDir, binaryName); - - if (this.binaryExists(binaryName, downloadDir)) { - await this.setExecutable(binaryPath); - return binaryPath; - } - - binaryPath = await this.downloadAndInstallStackQL({ - downloadDir, - binaryName, - }); - return binaryPath; - } catch (error) { - console.error(`ERROR: [setup] ${error.message}`); - Deno.exit(1); - } - } - - private async removeStackQL() { - const downloadDir = this.getDownloadDir(); - await Deno.remove(join(downloadDir, "/"), { recursive: true }); - console.log("stackql download dir removed"); - } - - public async upgradeStackQL() { - if (Deno.build.os === "darwin") { - await this.removeStackQL(); - } - const binaryName = this.getBinaryName(); - const downloadDir = this.getDownloadDir(); - await this.createDownloadDir(downloadDir); - const binaryPath = await this.downloadAndInstallStackQL({ - downloadDir, - binaryName, - }); - return binaryPath; - } + private os: string + private arch: string + private urlMap: Record + constructor() { + this.os = Deno.build.os // 'linux', 'darwin', or 'windows' + this.arch = Deno.build.arch // 'x86_64', 'arm64', etc. + + this.urlMap = { + [SupportedOs.Linux]: + 'https://releases.stackql.io/stackql/latest/stackql_linux_amd64.zip', + [SupportedOs.Windows]: + 'https://releases.stackql.io/stackql/latest/stackql_windows_amd64.zip', + [SupportedOs.Darwin]: + 'https://storage.googleapis.com/stackql-public-releases/latest/stackql_darwin_multiarch.pkg', + // Additional OS-architecture combinations can be added here + } + } + + private async downloadFile(url: string, downloadDir: string) { + const res = await fetch(url) + // create dir if not exists + + const file = await Deno.open(downloadDir, { create: true, write: true }) + + try { + await res.body?.pipeTo(file.writable).finally( + () => file.close(), //TODO: fix bad resource id when closing file + ) + } catch (error) { + console.error(`ERROR: [downloadFile] ${error.message}`) + } + + console.log('Closed file') + } + + private getUrl(): string { + const key = `${this.os}` + const url = this.urlMap[key] + + if (!url) { + throw new Error(`Unsupported OS type: ${this.os}`) + } + + return url + } + + /** + * Gets binary name + * @returns binrary name + */ + private getBinaryName() { + const binaryMap: Record = { + [SupportedOs.Windows]: 'stackql.exe', + [SupportedOs.Darwin]: 'stackql/Payload/stackql', + [SupportedOs.Linux]: 'stackql', // Default case for Linux and other platforms + } + const os = Deno.build.os.toLowerCase() + + if (!Object.values(SupportedOs).includes(os as SupportedOs)) { + throw new Error(`Unsupported OS type: ${os}`) + } + + const binaryOs = os as SupportedOs + return binaryMap[binaryOs] + } + + private async createDownloadDir(downloadDir: string) { + try { + const stat = await Deno.stat(downloadDir) + if (!stat.isDirectory) { + await Deno.mkdir(downloadDir, { recursive: true }) + } + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + await Deno.mkdir(downloadDir, { recursive: true }) + } else { + throw error + } + } + } + /** + * Gets download dir + * @returns download dir + */ + private getDownloadDir(): string { + const projectDir = Deno.cwd() + + if (!projectDir) { + throw new Error('Unable to determine the project directory.') + } + + const downloadDir = join(projectDir, '.stackql') + + return downloadDir + } + + private binaryExists(binaryName: string, downloadDir: string): boolean { + const binPath = join(downloadDir, binaryName) + try { + Deno.statSync(binPath) + return true + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + return false + } + throw error + } + } + private async installStackQL(downloadDir: string) { + const url = this.getUrl() + + const archiveFileName = `${downloadDir}/${url.split('/').pop()}` + await this.downloadFile(url, archiveFileName) + + console.log('Unpacking stackql binary') + const unpacker = Deno.build.os === 'darwin' ? darwinUnpack : unzip + await unpacker({ downloadDir, archiveFileName }) + } + + private async setExecutable(binaryPath: string) { + const allowExecOctal = 0o755 + await osUtils.chomod(binaryPath, allowExecOctal) + } + + private async downloadAndInstallStackQL({ + downloadDir, + binaryName, + }: { + downloadDir: string + binaryName: string + }) { + const binaryPath = join(downloadDir, binaryName) + await this.installStackQL(downloadDir) + await this.setExecutable(binaryPath) + return binaryPath + } + /** + * Setup stackql binary, check if binary exists, if not download it + */ + public async setupStackQL() { + try { + const binaryName = this.getBinaryName() + const downloadDir = this.getDownloadDir() + await this.createDownloadDir(downloadDir) + + let binaryPath = join(downloadDir, binaryName) + + if (this.binaryExists(binaryName, downloadDir)) { + await this.setExecutable(binaryPath) + return binaryPath + } + + binaryPath = await this.downloadAndInstallStackQL({ + downloadDir, + binaryName, + }) + return binaryPath + } catch (error) { + console.error(`ERROR: [setup] ${error.message}`) + Deno.exit(1) + } + } + + private async removeStackQL() { + const downloadDir = this.getDownloadDir() + await Deno.remove(join(downloadDir, '/'), { recursive: true }) + console.log('stackql download dir removed') + } + + public async upgradeStackQL() { + if (Deno.build.os === 'darwin') { + await this.removeStackQL() + } + const binaryName = this.getBinaryName() + const downloadDir = this.getDownloadDir() + await this.createDownloadDir(downloadDir) + const binaryPath = await this.downloadAndInstallStackQL({ + downloadDir, + binaryName, + }) + return binaryPath + } } diff --git a/src/services/server.test.ts b/src/services/server.test.ts index 30f31a9..74fe8b5 100644 --- a/src/services/server.test.ts +++ b/src/services/server.test.ts @@ -1,14 +1,14 @@ -import { Server } from "./server.ts"; -import { assert } from "https://deno.land/std@0.207.0/assert/assert.ts"; -import { startStackQLServer } from "../../testing/utils.ts"; +import { Server } from './server.ts' +import { assert } from 'https://deno.land/std@0.207.0/assert/assert.ts' +import { startStackQLServer } from '../../testing/utils.ts' -Deno.test("Successful Connection", async () => { - const { closeProcess } = await startStackQLServer(); - const server = new Server(); - const pg = await server.connect( - "postgres://postgres:password@localhost:5444/postgres", - ); - assert(pg); - await server.close(); - await closeProcess(); -}); +Deno.test('Successful Connection', async () => { + const { closeProcess } = await startStackQLServer() + const server = new Server() + const pg = await server.connect( + 'postgres://postgres:password@localhost:5444/postgres', + ) + assert(pg) + await server.close() + await closeProcess() +}) diff --git a/src/services/server.ts b/src/services/server.ts index 813be33..2a64961 100644 --- a/src/services/server.ts +++ b/src/services/server.ts @@ -1,53 +1,55 @@ -import { Client } from "https://deno.land/x/postgres/mod.ts"; +import { Client } from 'https://deno.land/x/postgres/mod.ts' export class Server { - private client: Client | null = null; - - constructor(connectionString?: string) { - if (connectionString) { - this.client = new Client(connectionString); - } - } - - public async connect(connectionString?: string) { - const maxRetries = 3; - let currentAttempt = 0; - - while (currentAttempt < maxRetries) { - try { - const connection = Deno.env.get("POSTGRES") || connectionString; - if (!connection) { - throw new Error( - "Connection string not found \n Please set the POSTGRES environment variable or pass the connection string as an argument", - ); - } - - console.log("connecting", connection); - this.client = new Client(connection); - await this.client.connect(); - console.log("connected"); - return this.client; - } catch (error) { - currentAttempt++; - console.log(`Attempt ${currentAttempt} failed: ${error.message}`); - - if (currentAttempt >= maxRetries) { - throw new Error( - `Could not connect to the server after ${maxRetries} attempts: ${error.message}`, - ); - } - - // Wait for 1 second before the next attempt - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - } - } - - public async close() { - if (this.client) { - await this.client.end(); - } - } - - // Additional methods for query execution can be added here + private client: Client | null = null + + constructor(connectionString?: string) { + if (connectionString) { + this.client = new Client(connectionString) + } + } + + public async connect(connectionString?: string) { + const maxRetries = 3 + let currentAttempt = 0 + + while (currentAttempt < maxRetries) { + try { + const connection = Deno.env.get('POSTGRES') || connectionString + if (!connection) { + throw new Error( + 'Connection string not found \n Please set the POSTGRES environment variable or pass the connection string as an argument', + ) + } + + console.log('connecting', connection) + this.client = new Client(connection) + await this.client.connect() + console.log('connected') + return this.client + } catch (error) { + currentAttempt++ + console.log( + `Attempt ${currentAttempt} failed: ${error.message}`, + ) + + if (currentAttempt >= maxRetries) { + throw new Error( + `Could not connect to the server after ${maxRetries} attempts: ${error.message}`, + ) + } + + // Wait for 1 second before the next attempt + await new Promise((resolve) => setTimeout(resolve, 1000)) + } + } + } + + public async close() { + if (this.client) { + await this.client.end() + } + } + + // Additional methods for query execution can be added here } diff --git a/src/services/unpacker.ts b/src/services/unpacker.ts index 25961d0..e93bc2f 100644 --- a/src/services/unpacker.ts +++ b/src/services/unpacker.ts @@ -1,37 +1,37 @@ -import { decompress } from "https://deno.land/x/zip/mod.ts"; +import { decompress } from 'https://deno.land/x/zip/mod.ts' type UnpackParams = { - downloadDir: string; - archiveFileName: string; -}; + downloadDir: string + archiveFileName: string +} export const darwinUnpack = async (params: UnpackParams) => { - console.log("darwinUnpack"); - const { downloadDir, archiveFileName } = params; - const unpackedFileName = `${downloadDir}/stackql`; - const commandPath = "pkgutil"; - const commandArgs = ["--expand-full", archiveFileName, unpackedFileName]; - const process = new Deno.Command(commandPath, { - args: commandArgs, - stdout: "piped", - stderr: "piped", - }); + console.log('darwinUnpack') + const { downloadDir, archiveFileName } = params + const unpackedFileName = `${downloadDir}/stackql` + const commandPath = 'pkgutil' + const commandArgs = ['--expand-full', archiveFileName, unpackedFileName] + const process = new Deno.Command(commandPath, { + args: commandArgs, + stdout: 'piped', + stderr: 'piped', + }) - const { code, stdout, stderr } = await process.output(); - if (code !== 0) { - const output = new TextDecoder().decode(stdout); - const errorOutput = new TextDecoder().decode(stderr); - console.error("Error executing pkgutil:", output, errorOutput); - throw new Error("Failed to unpack stackql"); - } -}; + const { code, stdout, stderr } = await process.output() + if (code !== 0) { + const output = new TextDecoder().decode(stdout) + const errorOutput = new TextDecoder().decode(stderr) + console.error('Error executing pkgutil:', output, errorOutput) + throw new Error('Failed to unpack stackql') + } +} export const unzip = async (params: UnpackParams) => { - console.log("unzip"); - try { - await decompress(params.archiveFileName, params.downloadDir); - } catch (error) { - console.log("[unzip] error:", error); - throw new Error("Failed to unpack stackql"); - } -}; + console.log('unzip') + try { + await decompress(params.archiveFileName, params.downloadDir) + } catch (error) { + console.log('[unzip] error:', error) + throw new Error('Failed to unpack stackql') + } +} diff --git a/src/stackql.test.ts b/src/stackql.test.ts index ae426d6..2762543 100644 --- a/src/stackql.test.ts +++ b/src/stackql.test.ts @@ -1,257 +1,254 @@ -import { assertStringIncludes } from "https://deno.land/std@0.206.0/assert/mod.ts"; -import { StackQL } from "./stackql.ts"; +import { assertStringIncludes } from 'https://deno.land/std@0.206.0/assert/mod.ts' +import { StackQL } from './stackql.ts' import { - isCsvString, - isJsonString, - startStackQLServer, -} from "../testing/utils.ts"; + isCsvString, + isJsonString, + startStackQLServer, +} from '../testing/utils.ts' import { - assertEquals, - assertExists, -} from "https://deno.land/std@0.160.0/testing/asserts.ts"; -import { Downloader } from "./services/downloader.ts"; + assertEquals, + assertExists, +} from 'https://deno.land/std@0.160.0/testing/asserts.ts' +import { Downloader } from './services/downloader.ts' import { - assertSpyCall, - spy, -} from "https://deno.land/std@0.207.0/testing/mock.ts"; -import osUtils from "./utils/os.ts"; -import { assert } from "https://deno.land/std@0.133.0/_util/assert.ts"; + assertSpyCall, + spy, +} from 'https://deno.land/std@0.207.0/testing/mock.ts' +import osUtils from './utils/os.ts' +import { assert } from 'https://deno.land/std@0.133.0/_util/assert.ts' -const downloader = new Downloader(); +const downloader = new Downloader() const setupStackQL = async () => { - await downloader.setupStackQL(); -}; - -Deno.test("StackQL CLI run query", async () => { - await setupStackQL(); - - // Arrange - const stackQL = new StackQL(); - await stackQL.initialize({ serverMode: false }); - - const pullQuery = "REGISTRY PULL github;"; - const providerQuery = "SHOW PROVIDERS"; - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'`; - - // Act - await stackQL.runQuery(pullQuery); - const result = await stackQL.runQuery(providerQuery); - const githubResult = await stackQL.runQuery(githubTestQuery); - - // Assert - assertStringIncludes(result, "name"); - assertStringIncludes(result, "version"); - assertStringIncludes(result, "github"); - assertStringIncludes(githubResult, "stackql"); -}); - -Deno.test("Set properties from configs", async () => { - await setupStackQL(); - const runCliSpy = spy(osUtils, "runCommand"); - const stackQL = new StackQL(); - await stackQL.initialize({ - serverMode: false, - maxResults: 100, - pageLimit: 10, - maxDepth: 5, - apiTimeout: 5000, - }); - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'`; - - await stackQL.runQuery(githubTestQuery); - - const params = stackQL.getParams(); - assertEquals(params.length, 8); - assertEquals(params, [ - "--http.response.maxResults", - "100", - "--http.response.pageLimit", - "10", - "--indirect.depth.max", - "5", - "--apirequesttimeout", - "5000", - ]); - const binaryPath = stackQL.getBinaryPath(); - assert(binaryPath); - assertSpyCall(runCliSpy, 0, { - args: [binaryPath, ["exec", githubTestQuery, ...params]], - }); - runCliSpy.restore(); -}); - -Deno.test("Set proxy properties from configs", async () => { - await setupStackQL(); - const runCommandSpy = spy(osUtils, "runCommand"); - const stackQL = new StackQL(); - await stackQL.initialize({ - serverMode: false, - proxyHost: "localhost", - proxyPort: 8080, - proxyUser: "user", - proxyPassword: "password", - proxyScheme: "https", - }); - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'`; - - await stackQL.runQuery(githubTestQuery); - - const params = stackQL.getParams(); - assertEquals(params, [ - "--http.proxy.host", - "localhost", - "--http.proxy.port", - "8080", - "--http.proxy.user", - "user", - "--http.proxy.password", - "password", - "--http.proxy.scheme", - "https", - ]); - const binaryPath = stackQL.getBinaryPath(); - assert(binaryPath); - assertSpyCall(runCommandSpy, 0, { - args: [binaryPath, ["exec", githubTestQuery, ...params]], - }); - runCommandSpy.restore(); -}); - -Deno.test("run query Output: json", async () => { - await setupStackQL(); - const stackQL = new StackQL(); - await stackQL.initialize({ - serverMode: false, - outputFormat: "json", - }); - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'`; - - const result = await stackQL.runQuery(githubTestQuery); - - const params = stackQL.getParams(); - - assertEquals(params, [ - "--output", - "json", - ]); - assert(isJsonString(result)); - assert(!(await isCsvString(result))); -}); - -Deno.test("run query Output: csv", async () => { - await setupStackQL(); - const stackQL = new StackQL(); - await stackQL.initialize({ - serverMode: false, - outputFormat: "csv", - }); - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'`; - - const result = await stackQL.runQuery(githubTestQuery); - const params = stackQL.getParams(); - - assertEquals(params, [ - "--output", - "csv", - ]); - assert(!isJsonString(result)); - assert(await isCsvString(result)); -}); - -Deno.test("StackQL runServerQuery", async () => { - const { closeProcess } = await startStackQLServer(); - const stackQL = new StackQL(); - - try { - // Arrange - await stackQL.initialize({ - serverMode: true, - connectionString: "postgres://postgres:password@localhost:5444/postgres", - }); - const pullQuery = "REGISTRY PULL github;"; - const testQuery = "SHOW SERVICES IN github LIKE '%repos%';"; // Replace with a valid query for your context - - // Act - await stackQL.runServerQuery(pullQuery); - const results = await stackQL.runServerQuery(testQuery); - assertExists(results); - assertEquals(results.length, 1); - const result = results[0] as { - name: string; - }; - assertEquals(result.name, "repos"); - - // Assert - } finally { - // Cleanup - await closeProcess(); - await stackQL.closeConnection(); - } -}); - -Deno.test("getVersion", async () => { - await setupStackQL(); - const stackQL = new StackQL(); - await stackQL.initialize({ serverMode: false }); - const versionRegex = /^v?(\d+(?:\.\d+)*)$/; - const shaRegex = /^[a-f0-9]{7}$/; - - const { version, sha } = await stackQL.getVersion(); - - assert(version); - assert(sha); - assert(versionRegex.test(version)); - assert(shaRegex.test(sha)); -}); - -Deno.test("getVersion when version and sha are undefined", async () => { - await setupStackQL(); - const stackQL = new StackQL(); - await stackQL.initialize({ serverMode: false }); - const versionRegex = /^v?(\d+(?:\.\d+)*)$/; - const shaRegex = /^[a-f0-9]{7}$/; - // deno-lint-ignore no-explicit-any - (stackQL as any).version = undefined; - // deno-lint-ignore no-explicit-any - (stackQL as any).sha = undefined; - // deno-lint-ignore no-explicit-any - assert((stackQL as any).version === undefined); - // deno-lint-ignore no-explicit-any - assert((stackQL as any).sha === undefined); - - const { version, sha } = await stackQL.getVersion(); - - assert(version); - assert(sha); - assert(versionRegex.test(version)); - assert(shaRegex.test(sha)); -}); - -Deno.test("upgrade stackql", async () => { - await setupStackQL(); - const stackQL = new StackQL(); - await stackQL.initialize({ serverMode: false }); - // deno-lint-ignore no-explicit-any - (stackQL as any).version = undefined; - // deno-lint-ignore no-explicit-any - (stackQL as any).sha = undefined; - const versionRegex = /^v?(\d+(?:\.\d+)*)$/; - const shaRegex = /^[a-f0-9]{7}$/; - // deno-lint-ignore no-explicit-any - assert((stackQL as any).version === undefined); - // deno-lint-ignore no-explicit-any - assert((stackQL as any).sha === undefined); - - const { version, sha } = await stackQL.upgrade(); - - assert(version); - assert(sha); - assert(versionRegex.test(version)); - assert(shaRegex.test(sha)); -}); + await downloader.setupStackQL() +} + +Deno.test('StackQL CLI run query', async () => { + await setupStackQL() + + // Arrange + const stackQL = new StackQL() + await stackQL.initialize({ serverMode: false }) + + const pullQuery = 'REGISTRY PULL github;' + const providerQuery = 'SHOW PROVIDERS' + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'` + + // Act + await stackQL.runQuery(pullQuery) + const result = await stackQL.runQuery(providerQuery) + const githubResult = await stackQL.runQuery(githubTestQuery) + + // Assert + assertStringIncludes(result, 'name') + assertStringIncludes(result, 'version') + assertStringIncludes(result, 'github') + assertStringIncludes(githubResult, 'stackql') +}) + +Deno.test('Set properties from configs', async () => { + await setupStackQL() + const runCliSpy = spy(osUtils, 'runCommand') + const stackQL = new StackQL() + await stackQL.initialize({ + serverMode: false, + maxResults: 100, + pageLimit: 10, + maxDepth: 5, + apiTimeout: 5000, + }) + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'` + + await stackQL.runQuery(githubTestQuery) + + const params = stackQL.getParams() + assertEquals(params.length, 8) + assertEquals(params, [ + '--http.response.maxResults', + '100', + '--http.response.pageLimit', + '10', + '--indirect.depth.max', + '5', + '--apirequesttimeout', + '5000', + ]) + const binaryPath = stackQL.getBinaryPath() + assert(binaryPath) + assertSpyCall(runCliSpy, 0, { + args: [binaryPath, ['exec', githubTestQuery, ...params]], + }) + runCliSpy.restore() +}) + +Deno.test('Set proxy properties from configs', async () => { + await setupStackQL() + const runCommandSpy = spy(osUtils, 'runCommand') + const stackQL = new StackQL() + await stackQL.initialize({ + serverMode: false, + proxyHost: 'localhost', + proxyPort: 8080, + proxyUser: 'user', + proxyPassword: 'password', + proxyScheme: 'https', + }) + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'` + + await stackQL.runQuery(githubTestQuery) + + const params = stackQL.getParams() + assertEquals(params, [ + '--http.proxy.host', + 'localhost', + '--http.proxy.port', + '8080', + '--http.proxy.user', + 'user', + '--http.proxy.password', + 'password', + '--http.proxy.scheme', + 'https', + ]) + const binaryPath = stackQL.getBinaryPath() + assert(binaryPath) + assertSpyCall(runCommandSpy, 0, { + args: [binaryPath, ['exec', githubTestQuery, ...params]], + }) + runCommandSpy.restore() +}) + +Deno.test('run query Output: json', async () => { + await setupStackQL() + const stackQL = new StackQL() + await stackQL.initialize({ + serverMode: false, + outputFormat: 'json', + }) + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'` + + const result = await stackQL.runQuery(githubTestQuery) + + const params = stackQL.getParams() + + assertEquals(params, [ + '--output', + 'json', + ]) + assert(isJsonString(result)) + assert(!(await isCsvString(result))) +}) + +Deno.test('run query Output: csv', async () => { + await setupStackQL() + const stackQL = new StackQL() + await stackQL.initialize({ + serverMode: false, + outputFormat: 'csv', + }) + const githubTestQuery = + `SELECT id, name from github.repos.repos where org='stackql'` + + const result = await stackQL.runQuery(githubTestQuery) + const params = stackQL.getParams() + + assertEquals(params, [ + '--output', + 'csv', + ]) + assert(!isJsonString(result)) + assert(await isCsvString(result)) +}) + +Deno.test('StackQL runServerQuery', async () => { + const { closeProcess } = await startStackQLServer() + const stackQL = new StackQL() + + try { + // Arrange + await stackQL.initialize({ + serverMode: true, + connectionString: + 'postgres://postgres:password@localhost:5444/postgres', + }) + const pullQuery = 'REGISTRY PULL github;' + const testQuery = 'SHOW SERVICES IN github LIKE \'%repos%\';' // Replace with a valid query for your context + + // Act + await stackQL.runServerQuery(pullQuery) + const results = await stackQL.runServerQuery(testQuery) + assertExists(results) + assertEquals(results.length, 1) + const result = results[0] as { + name: string + } + assertEquals(result.name, 'repos') + + // Assert + } finally { + // Cleanup + await closeProcess() + await stackQL.closeConnection() + } +}) + +Deno.test('getVersion', async () => { + await setupStackQL() + const stackQL = new StackQL() + await stackQL.initialize({ serverMode: false }) + const versionRegex = /^v?(\d+(?:\.\d+)*)$/ + const shaRegex = /^[a-f0-9]{7}$/ + + const { version, sha } = await stackQL.getVersion() + + assert(version) + assert(sha) + assert(versionRegex.test(version)) + assert(shaRegex.test(sha)) +}) + +Deno.test('getVersion when version and sha are undefined', async () => { + await setupStackQL() + const stackQL = new StackQL() + await stackQL.initialize({ serverMode: false }) + const versionRegex = /^v?(\d+(?:\.\d+)*)$/ + const shaRegex = /^[a-f0-9]{7}$/ // deno-lint-ignore no-explicit-any + ;(stackQL as any).version = undefined // deno-lint-ignore no-explicit-any + ;(stackQL as any).sha = undefined + // deno-lint-ignore no-explicit-any + assert((stackQL as any).version === undefined) + // deno-lint-ignore no-explicit-any + assert((stackQL as any).sha === undefined) + + const { version, sha } = await stackQL.getVersion() + + assert(version) + assert(sha) + assert(versionRegex.test(version)) + assert(shaRegex.test(sha)) +}) + +Deno.test('upgrade stackql', async () => { + await setupStackQL() + const stackQL = new StackQL() + await stackQL.initialize({ serverMode: false }) // deno-lint-ignore no-explicit-any + ;(stackQL as any).version = undefined // deno-lint-ignore no-explicit-any + ;(stackQL as any).sha = undefined + const versionRegex = /^v?(\d+(?:\.\d+)*)$/ + const shaRegex = /^[a-f0-9]{7}$/ + // deno-lint-ignore no-explicit-any + assert((stackQL as any).version === undefined) + // deno-lint-ignore no-explicit-any + assert((stackQL as any).sha === undefined) + + const { version, sha } = await stackQL.upgrade() + + assert(version) + assert(sha) + assert(versionRegex.test(version)) + assert(shaRegex.test(sha)) +}) diff --git a/src/stackql.ts b/src/stackql.ts index 353aa8c..981696e 100644 --- a/src/stackql.ts +++ b/src/stackql.ts @@ -1,228 +1,231 @@ -import { assertExists } from "https://deno.land/std@0.206.0/assert/assert_exists.ts"; -import { Downloader } from "./services/downloader.ts"; -import osUtils from "./utils/os.ts"; -import { Server } from "./services/server.ts"; -import { Client } from "https://deno.land/x/postgres@v0.17.0/client.ts"; +import { assertExists } from 'https://deno.land/std@0.206.0/assert/assert_exists.ts' +import { Downloader } from './services/downloader.ts' +import osUtils from './utils/os.ts' +import { Server } from './services/server.ts' +import { Client } from 'https://deno.land/x/postgres@v0.17.0/client.ts' export interface StackQLConfig { - binaryPath?: string; - serverMode?: boolean; - connectionString?: string; - maxResults?: number; - pageLimit?: number; - maxDepth?: number; - apiTimeout?: number; - proxyHost?: string; - proxyPort?: number; - proxyUser?: string; - proxyPassword?: string; - proxyScheme?: "http" | "https"; - outputFormat?: "csv" | "json"; + binaryPath?: string + serverMode?: boolean + connectionString?: string + maxResults?: number + pageLimit?: number + maxDepth?: number + apiTimeout?: number + proxyHost?: string + proxyPort?: number + proxyUser?: string + proxyPassword?: string + proxyScheme?: 'http' | 'https' + outputFormat?: 'csv' | 'json' } export class StackQL { - private binaryPath?: string; //The full path of the `stackql` executable (not supported in `server_mode`). - private downloader: Downloader = new Downloader(); - private serverMode = false; - private connection?: Client; - private outputFormat: "csv" | "json" = "json"; - private params: string[] = []; - private version: string | undefined; // The version number of the `stackql` executable (not supported in `server_mode`) - private sha: string | undefined; // The commit (short) sha for the installed `stackql` binary build (not supported in `server_mode`). - constructor() { - } - - getParams() { - return this.params; - } - - getBinaryPath() { - return this.binaryPath; - } - - async getVersion() { - if (!this.version) { - await this.updateVersion(); - } - - return { version: this.version, sha: this.sha }; - } - - async execute(query: string) { - if (this.serverMode) { - const result = await this.runServerQuery(query); - } - return await this.runQuery(query); - } - - private async updateVersion() { - if (!this.binaryPath) { - throw new Error("Binary path not found"); - } - const output = await osUtils.runCommand(this.binaryPath, ["--version"]); - if (output) { - const versionTokens: string[] = output.split("\n")[0].split(" "); - const version: string = versionTokens[1]; - const sha: string = versionTokens[3].replace("(", "").replace(")", ""); - - this.version = version; - this.sha = sha; - } - } - - /** - * Upgrade the `stackql` executable to the latest version - * @returns The version number of the `stackql` executable (not supported in `server_mode`) - */ - async upgrade() { - this.binaryPath = await this.downloader.upgradeStackQL(); - await this.updateVersion(); - return this.getVersion(); - } - - /** - * Initialize the `stackql` executable - * @param config The configuration object - * @returns The binary path of the `stackql` executable if `server_mode` is `false`, otherwise `undefined` - */ - public async initialize(config: StackQLConfig) { - this.binaryPath = config.binaryPath; - this.serverMode = config.serverMode || false; - if (this.serverMode) { - await this.setupConnection(config.connectionString); - return; - } - if (this.binaryExist()) { - return; - } - this.binaryPath = await this.downloader.setupStackQL(); - this.setProperties(config); - } - - private binaryExist() { - return !!this.binaryPath && osUtils.fileExists(this.binaryPath); - } - private setProxyProperties(config: StackQLConfig): void { - if (config.proxyHost !== undefined) { - this.params.push("--http.proxy.host"); - this.params.push(config.proxyHost); - } - - if (config.proxyPort !== undefined) { - this.params.push("--http.proxy.port"); - this.params.push(config.proxyPort.toString()); - } - - if (config.proxyUser !== undefined) { - this.params.push("--http.proxy.user"); - this.params.push(config.proxyUser); - } - - if (config.proxyPassword !== undefined) { - this.params.push("--http.proxy.password"); - this.params.push(config.proxyPassword); - } - - if (config.proxyScheme !== undefined) { - if (!["http", "https"].includes(config.proxyScheme)) { - throw new Error( - `Invalid proxyScheme. Expected one of ['http', 'https'], got ${config.proxyScheme}.`, - ); - } - this.params.push("--http.proxy.scheme"); - this.params.push(config.proxyScheme); - } - } - - private setProperties(config: StackQLConfig): void { - if (config.maxResults !== undefined) { - this.params.push("--http.response.maxResults"); - this.params.push(config.maxResults.toString()); - } - - if (config.pageLimit !== undefined) { - this.params.push("--http.response.pageLimit"); - this.params.push(config.pageLimit.toString()); - } - - if (config.maxDepth !== undefined) { - this.params.push("--indirect.depth.max"); - this.params.push(config.maxDepth.toString()); - } - - if (config.apiTimeout !== undefined) { - this.params.push("--apirequesttimeout"); - this.params.push(config.apiTimeout.toString()); - } - - if (config.proxyHost !== undefined) { - this.setProxyProperties(config); - } - - if (config.outputFormat !== undefined) { - if (!["csv", "json"].includes(config.outputFormat)) { - throw new Error( - `Invalid outputFormat. Expected one of ['csv', 'json'], got ${config.outputFormat}.`, - ); - } - this.params.push("--output"); - this.params.push(config.outputFormat); - this.outputFormat = config.outputFormat; - } - } - - /** - * Run a StackQL query - * @param query The StackQL query - * @returns The result of the query - */ - public async runQuery(query: string) { - assertExists(this.binaryPath); - const args = ["exec", query].concat(this.params); - try { - const result = await osUtils.runCommand(this.binaryPath, args); - return result; - } catch (error) { - console.error(error); - throw new Error(`StackQL query failed: ${error.message}`); - } - } - - //////////////////////Server mode related methods - private async queryObjectFormat(query: string) { - assertExists(this.connection); - const pgResult = await this.connection.queryObject(query); - return pgResult.rows; - } - - private async setupConnection(connectionString?: string) { - const server = new Server(); - this.connection = await server.connect(connectionString); - } - - /** - * Close the connection to the stackql server - */ - public async closeConnection() { - if (this.connection) { - await this.connection.end(); - } - } - - /** - * Run a StackQL query on the server - * @param query The StackQL query - * @returns The result of the query - */ - public async runServerQuery(query: string) { - try { - if (this.outputFormat === "json") { - const result = await this.queryObjectFormat(query); - return result; - } - } catch (error) { - console.error(error); - throw new Error(`StackQL server query failed: ${error.message}`); - } - } + private binaryPath?: string //The full path of the `stackql` executable (not supported in `server_mode`). + private downloader: Downloader = new Downloader() + private serverMode = false + private connection?: Client + private outputFormat: 'csv' | 'json' = 'json' + private params: string[] = [] + private version: string | undefined // The version number of the `stackql` executable (not supported in `server_mode`) + private sha: string | undefined // The commit (short) sha for the installed `stackql` binary build (not supported in `server_mode`). + constructor() { + } + + getParams() { + return this.params + } + + getBinaryPath() { + return this.binaryPath + } + + async getVersion() { + if (!this.version) { + await this.updateVersion() + } + + return { version: this.version, sha: this.sha } + } + + async execute(query: string) { + if (this.serverMode) { + const result = await this.runServerQuery(query) + } + return await this.runQuery(query) + } + + private async updateVersion() { + if (!this.binaryPath) { + throw new Error('Binary path not found') + } + const output = await osUtils.runCommand(this.binaryPath, ['--version']) + if (output) { + const versionTokens: string[] = output.split('\n')[0].split(' ') + const version: string = versionTokens[1] + const sha: string = versionTokens[3].replace('(', '').replace( + ')', + '', + ) + + this.version = version + this.sha = sha + } + } + + /** + * Upgrade the `stackql` executable to the latest version + * @returns The version number of the `stackql` executable (not supported in `server_mode`) + */ + async upgrade() { + this.binaryPath = await this.downloader.upgradeStackQL() + await this.updateVersion() + return this.getVersion() + } + + /** + * Initialize the `stackql` executable + * @param config The configuration object + * @returns The binary path of the `stackql` executable if `server_mode` is `false`, otherwise `undefined` + */ + public async initialize(config: StackQLConfig) { + this.binaryPath = config.binaryPath + this.serverMode = config.serverMode || false + if (this.serverMode) { + await this.setupConnection(config.connectionString) + return + } + if (this.binaryExist()) { + return + } + this.binaryPath = await this.downloader.setupStackQL() + this.setProperties(config) + } + + private binaryExist() { + return !!this.binaryPath && osUtils.fileExists(this.binaryPath) + } + private setProxyProperties(config: StackQLConfig): void { + if (config.proxyHost !== undefined) { + this.params.push('--http.proxy.host') + this.params.push(config.proxyHost) + } + + if (config.proxyPort !== undefined) { + this.params.push('--http.proxy.port') + this.params.push(config.proxyPort.toString()) + } + + if (config.proxyUser !== undefined) { + this.params.push('--http.proxy.user') + this.params.push(config.proxyUser) + } + + if (config.proxyPassword !== undefined) { + this.params.push('--http.proxy.password') + this.params.push(config.proxyPassword) + } + + if (config.proxyScheme !== undefined) { + if (!['http', 'https'].includes(config.proxyScheme)) { + throw new Error( + `Invalid proxyScheme. Expected one of ['http', 'https'], got ${config.proxyScheme}.`, + ) + } + this.params.push('--http.proxy.scheme') + this.params.push(config.proxyScheme) + } + } + + private setProperties(config: StackQLConfig): void { + if (config.maxResults !== undefined) { + this.params.push('--http.response.maxResults') + this.params.push(config.maxResults.toString()) + } + + if (config.pageLimit !== undefined) { + this.params.push('--http.response.pageLimit') + this.params.push(config.pageLimit.toString()) + } + + if (config.maxDepth !== undefined) { + this.params.push('--indirect.depth.max') + this.params.push(config.maxDepth.toString()) + } + + if (config.apiTimeout !== undefined) { + this.params.push('--apirequesttimeout') + this.params.push(config.apiTimeout.toString()) + } + + if (config.proxyHost !== undefined) { + this.setProxyProperties(config) + } + + if (config.outputFormat !== undefined) { + if (!['csv', 'json'].includes(config.outputFormat)) { + throw new Error( + `Invalid outputFormat. Expected one of ['csv', 'json'], got ${config.outputFormat}.`, + ) + } + this.params.push('--output') + this.params.push(config.outputFormat) + this.outputFormat = config.outputFormat + } + } + + /** + * Run a StackQL query + * @param query The StackQL query + * @returns The result of the query + */ + public async runQuery(query: string) { + assertExists(this.binaryPath) + const args = ['exec', query].concat(this.params) + try { + const result = await osUtils.runCommand(this.binaryPath, args) + return result + } catch (error) { + console.error(error) + throw new Error(`StackQL query failed: ${error.message}`) + } + } + + //////////////////////Server mode related methods + private async queryObjectFormat(query: string) { + assertExists(this.connection) + const pgResult = await this.connection.queryObject(query) + return pgResult.rows + } + + private async setupConnection(connectionString?: string) { + const server = new Server() + this.connection = await server.connect(connectionString) + } + + /** + * Close the connection to the stackql server + */ + public async closeConnection() { + if (this.connection) { + await this.connection.end() + } + } + + /** + * Run a StackQL query on the server + * @param query The StackQL query + * @returns The result of the query + */ + public async runServerQuery(query: string) { + try { + if (this.outputFormat === 'json') { + const result = await this.queryObjectFormat(query) + return result + } + } catch (error) { + console.error(error) + throw new Error(`StackQL server query failed: ${error.message}`) + } + } } diff --git a/src/types/platforms.ts b/src/types/platforms.ts index 8948c55..7688704 100644 --- a/src/types/platforms.ts +++ b/src/types/platforms.ts @@ -1,5 +1,5 @@ export enum SupportedOs { - Linux = "linux", - Darwin = "darwin", - Windows = "windows", -} \ No newline at end of file + Linux = 'linux', + Darwin = 'darwin', + Windows = 'windows', +} diff --git a/src/utils/os.test.ts b/src/utils/os.test.ts index 1183499..98ebd24 100644 --- a/src/utils/os.test.ts +++ b/src/utils/os.test.ts @@ -1,24 +1,24 @@ import { - assertRejects, - assertStringIncludes, -} from "https://deno.land/std@0.160.0/testing/asserts.ts"; -import osUtils from "./os.ts"; + assertRejects, + assertStringIncludes, +} from 'https://deno.land/std@0.160.0/testing/asserts.ts' +import osUtils from './os.ts' -Deno.test("osUtils.runCommand: Test Successful Execution", async () => { - const command = Deno.build.os === "windows" ? "cmd" : "echo"; - const args = Deno.build.os === "windows" - ? ["/c", "echo", "Hello, World!"] - : ["Hello, World!"]; - const result = await osUtils.runCommand(command, args); - assertStringIncludes(result.trim(), "Hello, World!"); -}); +Deno.test('osUtils.runCommand: Test Successful Execution', async () => { + const command = Deno.build.os === 'windows' ? 'cmd' : 'echo' + const args = Deno.build.os === 'windows' + ? ['/c', 'echo', 'Hello, World!'] + : ['Hello, World!'] + const result = await osUtils.runCommand(command, args) + assertStringIncludes(result.trim(), 'Hello, World!') +}) -Deno.test("osUtils.runCommand: Test Failed Execution", () => { - const command = "invalid"; - const args = Deno.build.os === "windows" - ? ["/c", "echo", "Hello, World!"] - : ["Hello, World!"]; - assertRejects(async () => { - await osUtils.runCommand(command, args); - }); -}); +Deno.test('osUtils.runCommand: Test Failed Execution', () => { + const command = 'invalid' + const args = Deno.build.os === 'windows' + ? ['/c', 'echo', 'Hello, World!'] + : ['Hello, World!'] + assertRejects(async () => { + await osUtils.runCommand(command, args) + }) +}) diff --git a/src/utils/os.ts b/src/utils/os.ts index 29d3f5d..a0876ca 100644 --- a/src/utils/os.ts +++ b/src/utils/os.ts @@ -1,48 +1,48 @@ const fileExists = (path?: string) => { - if (!path) { - return false; - } - try { - Deno.statSync(path); - return true; - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - return false; - } - throw error; - } -}; + if (!path) { + return false + } + try { + Deno.statSync(path) + return true + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + return false + } + throw error + } +} const chomod = async (path: string, mode: number) => { - if (Deno.build.os !== "windows") { - await Deno.chmod(path, mode); - } -}; + if (Deno.build.os !== 'windows') { + await Deno.chmod(path, mode) + } +} const runCommand = async (path: string, args: string[]) => { - const process = new Deno.Command(path, { - args, - stdout: "piped", - stderr: "piped", - }); + const process = new Deno.Command(path, { + args, + stdout: 'piped', + stderr: 'piped', + }) - const { code, stdout, stderr } = await process.output(); + const { code, stdout, stderr } = await process.output() - if (code === 0) { - const output = stdout; - const result = new TextDecoder().decode(output); - return result; - } else { - const errorOutput = stderr; - const errorMessage = new TextDecoder().decode(errorOutput); - throw new Error(errorMessage); - } -}; + if (code === 0) { + const output = stdout + const result = new TextDecoder().decode(output) + return result + } else { + const errorOutput = stderr + const errorMessage = new TextDecoder().decode(errorOutput) + throw new Error(errorMessage) + } +} const osUtils = { - fileExists, - chomod, - runCommand, -}; + fileExists, + chomod, + runCommand, +} -export default osUtils; +export default osUtils From 4b52920cddef8baa0e5f3800c7c3bec280878abd Mon Sep 17 00:00:00 2001 From: Yuncheng Yang <45674168+yunchengyang515@users.noreply.github.com> Date: Sat, 9 Dec 2023 15:13:18 +1100 Subject: [PATCH 6/6] Revert "Minor fix/add fmt on save" --- .github/workflows/test.yml | 25 ---- .gitignore | 3 +- .vscode/settings.json | 7 - README.md | 22 +-- deno.json | 24 --- deno.lock | 178 ---------------------- deps.ts | 2 +- mod.ts | 6 +- src/services/downloader.test.ts | 49 ------ src/services/downloader.ts | 189 ------------------------ src/services/server.test.ts | 14 -- src/services/server.ts | 55 ------- src/services/unpacker.ts | 37 ----- src/stackql.test.ts | 254 -------------------------------- src/stackql.ts | 231 ----------------------------- src/types/platforms.ts | 5 - src/utils/os.test.ts | 24 --- src/utils/os.ts | 48 ------ testing/utils.ts | 63 -------- 19 files changed, 12 insertions(+), 1224 deletions(-) delete mode 100644 .github/workflows/test.yml delete mode 100644 .vscode/settings.json delete mode 100644 deno.json delete mode 100644 deno.lock delete mode 100644 src/services/downloader.test.ts delete mode 100644 src/services/downloader.ts delete mode 100644 src/services/server.test.ts delete mode 100644 src/services/server.ts delete mode 100644 src/services/unpacker.ts delete mode 100644 src/stackql.test.ts delete mode 100644 src/stackql.ts delete mode 100644 src/types/platforms.ts delete mode 100644 src/utils/os.test.ts delete mode 100644 src/utils/os.ts delete mode 100644 testing/utils.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index a0adbaf..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Test - -on: - push: - -jobs: - test: - name: Test on Multiple OS - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Set up Deno - uses: denoland/setup-deno@v1 - with: - deno-version: v1.x - - name: setup StackQL - uses: stackql/setup-stackql@v2.0.0 - with: - use_wrapper: false - - name: Run tests - run: deno test --allow-net --allow-read --allow-write --allow-env --allow-run diff --git a/.gitignore b/.gitignore index f867e14..46f26a8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,5 +33,4 @@ yarn-error.log* # typescript *.tsbuildinfo -next-env.d.ts -.stackql* \ No newline at end of file +next-env.d.ts \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 22ae4cf..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "deno.enable": true, - "deno.lint": true, - "deno.unstable": true, - "editor.formatOnSave": true, - "editor.defaultFormatter": "denoland.vscode-deno" -} diff --git a/README.md b/README.md index 83db40c..4c0a490 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # stackqljs -[**StackQL**](https://github.com/stackql/stackql) client library for Deno and Node.js that exposes all features StackQL. +[__StackQL__](https://github.com/stackql/stackql) client library for Deno and Node.js that exposes all features StackQL. ## Requirements @@ -14,18 +14,8 @@ - No dependencies - Works in server mode (as a pg wire protocol server client) as well as local mode where it is a wrapper for the `stackql` binary for the target platform - Exposes methods analagous to [`pystackql`](https://pystackql.readthedocs.io/en/latest/), including: - - `connect` - connect to a StackQL server - if in server mode - - `upgrade` - if in local mode, upgrade the local stackql binary from the latest release - - `execute` - execute a query returns an array of rows (objects) - - `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`) - - `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema - -## Test - -### Requirement - -- To run the tests locally, [install StackQL](https://stackql.io/docs/installing-stackql) first. - -``` -deno task test -``` + - `connect` - connect to a StackQL server - if in server mode + - `upgrade` - if in local mode, upgrade the local stackql binary from the latest release + - `execute` - execute a query returns an array of rows (objects) + - `executeStmt` - executes a statement and returns a string (like `REGISTRY PULL` or `INSERT ...`) + - `executeQueriesAsync` - executes a list of queries and returns an array of rows (objects) - queries need to return the same columns/schema \ No newline at end of file diff --git a/deno.json b/deno.json deleted file mode 100644 index cdafc9b..0000000 --- a/deno.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "lint": { - "include": ["src/"], - "exclude": ["src/testing/", "src/**/*.test.ts", "src/*test.ts/"], - "rules": { - "tags": ["recommended"], - "include": [], - "exclude": ["no-unused-vars", "ban-untagged-todo"] - } - }, - "fmt": { - "useTabs": true, - "lineWidth": 80, - "indentWidth": 4, - "semiColons": false, - "singleQuote": true, - "proseWrap": "preserve", - "include": ["src/"], - "exclude": ["src/testdata/", "data/fixtures/**/*.ts"] - }, - "tasks": { - "test": "deno test --allow-net --allow-read --allow-write --allow-env --allow-run" - } -} diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 9f2461c..0000000 --- a/deno.lock +++ /dev/null @@ -1,178 +0,0 @@ -{ - "version": "2", - "remote": { - "https://deno.land/std@0.133.0/_deno_unstable.ts": "23a1a36928f1b6d3b0170aaa67de09af12aa998525f608ff7331b9fb364cbde6", - "https://deno.land/std@0.133.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", - "https://deno.land/std@0.133.0/_util/os.ts": "49b92edea1e82ba295ec946de8ffd956ed123e2948d9bd1d3e901b04e4307617", - "https://deno.land/std@0.133.0/fs/_util.ts": "0fb24eb4bfebc2c194fb1afdb42b9c3dda12e368f43e8f2321f84fc77d42cb0f", - "https://deno.land/std@0.133.0/fs/copy.ts": "9248d1492599957af8c693ceb10a432b09f0b0b61c60a4d6aff29b0c7d3a17b3", - "https://deno.land/std@0.133.0/fs/empty_dir.ts": "7274d87160de34cbed0531e284df383045cf43543bbeadeb97feac598bd8f3c5", - "https://deno.land/std@0.133.0/fs/ensure_dir.ts": "9dc109c27df4098b9fc12d949612ae5c9c7169507660dcf9ad90631833209d9d", - "https://deno.land/std@0.133.0/fs/ensure_file.ts": "7d353e64fee3d4d1e7c6b6726a2a5e987ba402c15fb49566309042887349c545", - "https://deno.land/std@0.133.0/fs/ensure_link.ts": "489e23df9fe3e6636048b5830ddf0f111eb29621eb85719255ad9bd645f3471b", - "https://deno.land/std@0.133.0/fs/ensure_symlink.ts": "88dc83de1bc90ed883dd458c2d2eae3d5834a4617d12925734836e1f0803b274", - "https://deno.land/std@0.133.0/fs/eol.ts": "b92f0b88036de507e7e6fbedbe8f666835ea9dcbf5ac85917fa1fadc919f83a5", - "https://deno.land/std@0.133.0/fs/exists.ts": "cb734d872f8554ea40b8bff77ad33d4143c1187eac621a55bf37781a43c56f6d", - "https://deno.land/std@0.133.0/fs/expand_glob.ts": "0c10130d67c9b02164b03df8e43c6d6defbf8e395cb69d09e84a8586e6d72ac3", - "https://deno.land/std@0.133.0/fs/mod.ts": "4dc052c461c171abb5c25f6e0f218ab838a716230930b534ba351745864b7d6d", - "https://deno.land/std@0.133.0/fs/move.ts": "0573cedcf583f09a9494f2dfccbf67de68a93629942d6b5e6e74a9e45d4e8a2e", - "https://deno.land/std@0.133.0/fs/walk.ts": "117403ccd21fd322febe56ba06053b1ad5064c802170f19b1ea43214088fe95f", - "https://deno.land/std@0.133.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", - "https://deno.land/std@0.133.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", - "https://deno.land/std@0.133.0/path/_util.ts": "c1e9686d0164e29f7d880b2158971d805b6e0efc3110d0b3e24e4b8af2190d2b", - "https://deno.land/std@0.133.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", - "https://deno.land/std@0.133.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", - "https://deno.land/std@0.133.0/path/mod.ts": "4275129bb766f0e475ecc5246aa35689eeade419d72a48355203f31802640be7", - "https://deno.land/std@0.133.0/path/posix.ts": "663e4a6fe30a145f56aa41a22d95114c4c5582d8b57d2d7c9ed27ad2c47636bb", - "https://deno.land/std@0.133.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", - "https://deno.land/std@0.133.0/path/win32.ts": "e7bdf63e8d9982b4d8a01ef5689425c93310ece950e517476e22af10f41a136e", - "https://deno.land/std@0.134.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", - "https://deno.land/std@0.134.0/bytes/bytes_list.ts": "67eb118e0b7891d2f389dad4add35856f4ad5faab46318ff99653456c23b025d", - "https://deno.land/std@0.134.0/bytes/equals.ts": "a60ef9f01fb6e06a4e0343fc44d53d39d12dd66bc3f09ac8e6eb9cc1a6335e48", - "https://deno.land/std@0.134.0/bytes/mod.ts": "4cef6fe8f0de217b9babbcbb0a566402b667f18a8e6d094a45e5fb3fc1afff70", - "https://deno.land/std@0.134.0/encoding/csv.ts": "5c7c20114cd4ca93d43226350f116e3261459ff288d232663c0adb4569eae95d", - "https://deno.land/std@0.134.0/encoding/csv/_io.ts": "91a61ee8ec8151126479453c71b7cb789fbb2bd224052360e39a79f81df1f3dc", - "https://deno.land/std@0.134.0/encoding/csv_stringify.ts": "b6397d6ab330b9954872558ac21a384376c2c4f29b86fbf56b9cdd7b462fa5dd", - "https://deno.land/std@0.134.0/io/buffer.ts": "bd0c4bf53db4b4be916ca5963e454bddfd3fcd45039041ea161dbf826817822b", - "https://deno.land/std@0.134.0/io/readers.ts": "679471f3b9929b54393c9cd75b6bd178b4bc6d9aab5c0f1f9538f862cf4746fe", - "https://deno.land/std@0.134.0/io/types.d.ts": "01f60ae7ec02675b5dbed150d258fc184a78dfe5c209ef53ba4422b46b58822c", - "https://deno.land/std@0.134.0/textproto/mod.ts": "b78acf898a4e1f35eac9a955e07c84c70824662d918df988b22c0abcf2a9a987", - "https://deno.land/std@0.160.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74", - "https://deno.land/std@0.160.0/_util/os.ts": "8a33345f74990e627b9dfe2de9b040004b08ea5146c7c9e8fe9a29070d193934", - "https://deno.land/std@0.160.0/async/abortable.ts": "87aa7230be8360c24ad437212311c9e8d4328854baec27b4c7abb26e85515c06", - "https://deno.land/std@0.160.0/async/deadline.ts": "48ac998d7564969f3e6ec6b6f9bf0217ebd00239b1b2292feba61272d5dd58d0", - "https://deno.land/std@0.160.0/async/debounce.ts": "dc8b92d4a4fe7eac32c924f2b8d3e62112530db70cadce27042689d82970b350", - "https://deno.land/std@0.160.0/async/deferred.ts": "d8fb253ffde2a056e4889ef7e90f3928f28be9f9294b6505773d33f136aab4e6", - "https://deno.land/std@0.160.0/async/delay.ts": "0419dfc993752849692d1f9647edf13407c7facc3509b099381be99ffbc9d699", - "https://deno.land/std@0.160.0/async/mod.ts": "dd0a8ed4f3984ffabe2fcca7c9f466b7932d57b1864ffee148a5d5388316db6b", - "https://deno.land/std@0.160.0/async/mux_async_iterator.ts": "3447b28a2a582224a3d4d3596bccbba6e85040da3b97ed64012f7decce98d093", - "https://deno.land/std@0.160.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239", - "https://deno.land/std@0.160.0/async/tee.ts": "9af3a3e7612af75861308b52249e167f5ebc3dcfc8a1a4d45462d96606ee2b70", - "https://deno.land/std@0.160.0/bytes/bytes_list.ts": "aba5e2369e77d426b10af1de0dcc4531acecec27f9b9056f4f7bfbf8ac147ab4", - "https://deno.land/std@0.160.0/bytes/equals.ts": "3c3558c3ae85526f84510aa2b48ab2ad7bdd899e2e0f5b7a8ffc85acb3a6043a", - "https://deno.land/std@0.160.0/bytes/mod.ts": "b2e342fd3669176a27a4e15061e9d588b89c1aaf5008ab71766e23669565d179", - "https://deno.land/std@0.160.0/crypto/_fnv/fnv32.ts": "aa9bddead8c6345087d3abd4ef35fb9655622afc333fc41fff382b36e64280b5", - "https://deno.land/std@0.160.0/crypto/_fnv/fnv64.ts": "625d7e7505b6cb2e9801b5fd6ed0a89256bac12b2bbb3e4664b85a88b0ec5bef", - "https://deno.land/std@0.160.0/crypto/_fnv/index.ts": "a8f6a361b4c6d54e5e89c16098f99b6962a1dd6ad1307dbc97fa1ecac5d7060a", - "https://deno.land/std@0.160.0/crypto/_fnv/util.ts": "4848313bed7f00f55be3cb080aa0583fc007812ba965b03e4009665bde614ce3", - "https://deno.land/std@0.160.0/crypto/_wasm_crypto/lib/deno_std_wasm_crypto.generated.mjs": "258b484c2da27578bec61c01d4b62c21f72268d928d03c968c4eb590cb3bd830", - "https://deno.land/std@0.160.0/crypto/_wasm_crypto/mod.ts": "6c60d332716147ded0eece0861780678d51b560f533b27db2e15c64a4ef83665", - "https://deno.land/std@0.160.0/crypto/keystack.ts": "e481eed28007395e554a435e880fee83a5c73b9259ed8a135a75e4b1e4f381f7", - "https://deno.land/std@0.160.0/crypto/mod.ts": "fadedc013b4a86fda6305f1adc6d1c02225834d53cff5d95cc05f62b25127517", - "https://deno.land/std@0.160.0/crypto/timing_safe_equal.ts": "82a29b737bc8932d75d7a20c404136089d5d23629e94ba14efa98a8cc066c73e", - "https://deno.land/std@0.160.0/datetime/formatter.ts": "7c8e6d16a0950f400aef41b9f1eb9168249869776ec520265dfda785d746589e", - "https://deno.land/std@0.160.0/datetime/mod.ts": "ea927ca96dfb28c7b9a5eed5bdc7ac46bb9db38038c4922631895cea342fea87", - "https://deno.land/std@0.160.0/datetime/tokenizer.ts": "7381e28f6ab51cb504c7e132be31773d73ef2f3e1e50a812736962b9df1e8c47", - "https://deno.land/std@0.160.0/encoding/base64.ts": "c57868ca7fa2fbe919f57f88a623ad34e3d970d675bdc1ff3a9d02bba7409db2", - "https://deno.land/std@0.160.0/encoding/base64url.ts": "a5f82a9fa703bd85a5eb8e7c1296bc6529e601ebd9642cc2b5eaa6b38fa9e05a", - "https://deno.land/std@0.160.0/encoding/hex.ts": "4cc5324417cbb4ac9b828453d35aed45b9cc29506fad658f1f138d981ae33795", - "https://deno.land/std@0.160.0/fmt/colors.ts": "9e36a716611dcd2e4865adea9c4bec916b5c60caad4cdcdc630d4974e6bb8bd4", - "https://deno.land/std@0.160.0/io/buffer.ts": "fae02290f52301c4e0188670e730cd902f9307fb732d79c4aa14ebdc82497289", - "https://deno.land/std@0.160.0/io/types.d.ts": "107e1e64834c5ba917c783f446b407d33432c5d612c4b3430df64fc2b4ecf091", - "https://deno.land/std@0.160.0/path/_constants.ts": "df1db3ffa6dd6d1252cc9617e5d72165cd2483df90e93833e13580687b6083c3", - "https://deno.land/std@0.160.0/path/_interface.ts": "ee3b431a336b80cf445441109d089b70d87d5e248f4f90ff906820889ecf8d09", - "https://deno.land/std@0.160.0/path/_util.ts": "d16be2a16e1204b65f9d0dfc54a9bc472cafe5f4a190b3c8471ec2016ccd1677", - "https://deno.land/std@0.160.0/path/common.ts": "bee563630abd2d97f99d83c96c2fa0cca7cee103e8cb4e7699ec4d5db7bd2633", - "https://deno.land/std@0.160.0/path/glob.ts": "cb5255638de1048973c3e69e420c77dc04f75755524cb3b2e160fe9277d939ee", - "https://deno.land/std@0.160.0/path/mod.ts": "56fec03ad0ebd61b6ab39ddb9b0ddb4c4a5c9f2f4f632e09dd37ec9ebfd722ac", - "https://deno.land/std@0.160.0/path/posix.ts": "6b63de7097e68c8663c84ccedc0fd977656eb134432d818ecd3a4e122638ac24", - "https://deno.land/std@0.160.0/path/separator.ts": "fe1816cb765a8068afb3e8f13ad272351c85cbc739af56dacfc7d93d710fe0f9", - "https://deno.land/std@0.160.0/path/win32.ts": "ee8826dce087d31c5c81cd414714e677eb68febc40308de87a2ce4b40e10fb8d", - "https://deno.land/std@0.160.0/testing/_diff.ts": "a23e7fc2b4d8daa3e158fa06856bedf5334ce2a2831e8bf9e509717f455adb2c", - "https://deno.land/std@0.160.0/testing/_format.ts": "cd11136e1797791045e639e9f0f4640d5b4166148796cad37e6ef75f7d7f3832", - "https://deno.land/std@0.160.0/testing/asserts.ts": "1e340c589853e82e0807629ba31a43c84ebdcdeca910c4a9705715dfdb0f5ce8", - "https://deno.land/std@0.188.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/std@0.188.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", - "https://deno.land/std@0.188.0/fs/_util.ts": "579038bebc3bd35c43a6a7766f7d91fbacdf44bc03468e9d3134297bb99ed4f9", - "https://deno.land/std@0.188.0/fs/copy.ts": "14214efd94fc3aa6db1e4af2b4b9578e50f7362b7f3725d5a14ad259a5df26c8", - "https://deno.land/std@0.188.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", - "https://deno.land/std@0.188.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", - "https://deno.land/std@0.188.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", - "https://deno.land/std@0.188.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", - "https://deno.land/std@0.188.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1", - "https://deno.land/std@0.188.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", - "https://deno.land/std@0.188.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", - "https://deno.land/std@0.188.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", - "https://deno.land/std@0.188.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", - "https://deno.land/std@0.188.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", - "https://deno.land/std@0.188.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", - "https://deno.land/std@0.188.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", - "https://deno.land/std@0.188.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", - "https://deno.land/std@0.188.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", - "https://deno.land/std@0.188.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", - "https://deno.land/std@0.188.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", - "https://deno.land/std@0.188.0/path/mod.ts": "ee161baec5ded6510ee1d1fb6a75a0f5e4b41f3f3301c92c716ecbdf7dae910d", - "https://deno.land/std@0.188.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", - "https://deno.land/std@0.188.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", - "https://deno.land/std@0.188.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", - "https://deno.land/std@0.206.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", - "https://deno.land/std@0.206.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", - "https://deno.land/std@0.206.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.206.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", - "https://deno.land/std@0.206.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", - "https://deno.land/std@0.206.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", - "https://deno.land/std@0.206.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", - "https://deno.land/std@0.206.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", - "https://deno.land/std@0.206.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", - "https://deno.land/std@0.206.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", - "https://deno.land/std@0.206.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", - "https://deno.land/std@0.206.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", - "https://deno.land/std@0.206.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", - "https://deno.land/std@0.206.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", - "https://deno.land/std@0.206.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", - "https://deno.land/std@0.206.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", - "https://deno.land/std@0.206.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", - "https://deno.land/std@0.206.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", - "https://deno.land/std@0.206.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", - "https://deno.land/std@0.206.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", - "https://deno.land/std@0.206.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", - "https://deno.land/std@0.206.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", - "https://deno.land/std@0.206.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", - "https://deno.land/std@0.206.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", - "https://deno.land/std@0.206.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", - "https://deno.land/std@0.206.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", - "https://deno.land/std@0.206.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", - "https://deno.land/std@0.206.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", - "https://deno.land/std@0.206.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", - "https://deno.land/std@0.206.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", - "https://deno.land/std@0.206.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", - "https://deno.land/std@0.206.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", - "https://deno.land/std@0.207.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", - "https://deno.land/std@0.207.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", - "https://deno.land/std@0.207.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.207.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", - "https://deno.land/std@0.207.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", - "https://deno.land/std@0.207.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", - "https://deno.land/std@0.207.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", - "https://deno.land/std@0.207.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", - "https://deno.land/std@0.207.0/fmt/colors.ts": "34b3f77432925eb72cf0bfb351616949746768620b8e5ead66da532f93d10ba2", - "https://deno.land/std@0.207.0/testing/mock.ts": "5c4363d6846179390c1ef4347ea0fddfd9780705089228cba331dc49c2a4f3c1", - "https://deno.land/x/postgres@v0.17.0/client.ts": "348779c9f6a1c75ef1336db662faf08dce7d2101ff72f0d1e341ba1505c8431d", - "https://deno.land/x/postgres@v0.17.0/client/error.ts": "0817583b666fd546664ed52c1d37beccc5a9eebcc6e3c2ead20ada99b681e5f7", - "https://deno.land/x/postgres@v0.17.0/connection/auth.ts": "1070125e2ac4ca4ade36d69a4222d37001903092826d313217987583edd61ce9", - "https://deno.land/x/postgres@v0.17.0/connection/connection.ts": "428ed3efa055870db505092b5d3545ef743497b7b4b72cf8f0593e7dd4788acd", - "https://deno.land/x/postgres@v0.17.0/connection/connection_params.ts": "52bfe90e8860f584b95b1b08c254dde97c3aa763c4b6bee0c80c5930e35459e0", - "https://deno.land/x/postgres@v0.17.0/connection/message.ts": "f9257948b7f87d58bfbfe3fc6e2e08f0de3ef885655904d56a5f73655cc22c5a", - "https://deno.land/x/postgres@v0.17.0/connection/message_code.ts": "466719008b298770c366c5c63f6cf8285b7f76514dadb4b11e7d9756a8a1ddbf", - "https://deno.land/x/postgres@v0.17.0/connection/packet.ts": "050aeff1fc13c9349e89451a155ffcd0b1343dc313a51f84439e3e45f64b56c8", - "https://deno.land/x/postgres@v0.17.0/connection/scram.ts": "0c7a2551fe7b1a1c62dd856b7714731a7e7534ccca10093336782d1bfc5b2bd2", - "https://deno.land/x/postgres@v0.17.0/deps.ts": "f47ccb41f7f97eaad455d94f407ef97146ae99443dbe782894422c869fbba69e", - "https://deno.land/x/postgres@v0.17.0/mod.ts": "a1e18fd9e6fedc8bc24e5aeec3ae6de45e2274be1411fb66e9081420c5e81d7d", - "https://deno.land/x/postgres@v0.17.0/pool.ts": "892db7b5e1787988babecc994a151ebbd7d017f080905cbe9c3d7b44a73032a9", - "https://deno.land/x/postgres@v0.17.0/query/array_parser.ts": "f8a229d82c3801de8266fa2cc4afe12e94fef8d0c479e73655c86ed3667ef33f", - "https://deno.land/x/postgres@v0.17.0/query/decode.ts": "44a4a6cbcf494ed91a4fecae38a57dce63a7b519166f02c702791d9717371419", - "https://deno.land/x/postgres@v0.17.0/query/decoders.ts": "16cb0e60227d86692931e315421b15768c78526e3aeb84e25fcc4111096de9fd", - "https://deno.land/x/postgres@v0.17.0/query/encode.ts": "5f1418a2932b7c2231556e4a5f5f56efef48728014070cfafe7656963f342933", - "https://deno.land/x/postgres@v0.17.0/query/oid.ts": "8c33e1325f34e4ca9f11a48b8066c8cfcace5f64bc1eb17ad7247af4936999e1", - "https://deno.land/x/postgres@v0.17.0/query/query.ts": "edb473cbcfeff2ee1c631272afb25d079d06b66b5853f42492725b03ffa742b6", - "https://deno.land/x/postgres@v0.17.0/query/transaction.ts": "8e75c3ce0aca97da7fe126e68f8e6c08d640e5c8d2016e62cee5c254bebe7fe8", - "https://deno.land/x/postgres@v0.17.0/query/types.ts": "a6dc8024867fe7ccb0ba4b4fa403ee5d474c7742174128c8e689c3b5e5eaa933", - "https://deno.land/x/postgres@v0.17.0/utils/deferred.ts": "dd94f2a57355355c47812b061a51b55263f72d24e9cb3fdb474c7519f4d61083", - "https://deno.land/x/postgres@v0.17.0/utils/utils.ts": "19c3527ddd5c6c4c49ae36397120274c7f41f9d3cbf479cb36065d23329e9f90", - "https://deno.land/x/zip@v1.2.5/compress.ts": "43d9f4440960d15a85aec58f5d365acc25530d3d4186b2f5f896c090ecac20e8", - "https://deno.land/x/zip@v1.2.5/decompress.ts": "0bce3d453726f686274fab3f6c19b72b5e74223a00d89c176b1de49a5dd5528d", - "https://deno.land/x/zip@v1.2.5/deps.ts": "79548387594b3ae1efaaa870b5a507c4d6bedede13dbd5d4ad42f6cda0aeef86", - "https://deno.land/x/zip@v1.2.5/mod.ts": "28eecbc3e1e5adf564f4aa465e64268713a05653104bacdcb04561533f8caf57", - "https://deno.land/x/zip@v1.2.5/utils.ts": "43c323f2b79f9db1976c5739bbb1f9cced20e8077ca7e7e703f9d01d4330bd9d" - } -} diff --git a/deps.ts b/deps.ts index a837720..6cb0d37 100644 --- a/deps.ts +++ b/deps.ts @@ -1 +1 @@ -export { bold } from "https://deno.land/std/fmt/colors.ts" \ No newline at end of file +export { bold } from "https://deno.land/std@0.206.0/fmt/colors.ts"; \ No newline at end of file diff --git a/mod.ts b/mod.ts index 9ef3689..4236625 100644 --- a/mod.ts +++ b/mod.ts @@ -1,3 +1,5 @@ -import { StackQL } from "./src/stackql.ts"; +import { bold } from "./deps.ts"; -export { StackQL }; +export function getHelloWorld(): string { + return bold("Hello World"); + } diff --git a/src/services/downloader.test.ts b/src/services/downloader.test.ts deleted file mode 100644 index 4295f2b..0000000 --- a/src/services/downloader.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { assertExists } from 'https://deno.land/std@0.206.0/assert/assert_exists.ts' -import { Downloader } from './downloader.ts' -import { removeStackQLDownload } from '../../testing/utils.ts' -import { - assertSpyCalls, - spy, -} from 'https://deno.land/std@0.207.0/testing/mock.ts' - -Deno.test('Downloader setupStackQL and upgrade Test', async () => { - // Arrange - await removeStackQLDownload() - const downloader = new Downloader() - let binaryPath: string - const denoOpenSpy = spy(Deno, 'open') - - // Act - const setupTest = async () => { - try { - binaryPath = await downloader.setupStackQL() - - // Assert - assertExists(binaryPath) - assertSpyCalls(denoOpenSpy, 1) - // Check if the binary exists after setupStackQL is called - - console.log( - 'Test passed: setupStackQL completed without errors and binary exists.', - ) - } catch (error) { - console.error('Test failed:', error) - throw error // This will cause the test to fail - } - } - - const upgradeTest = async () => { - try { - binaryPath = await downloader.upgradeStackQL() - - assertExists(binaryPath) - assertSpyCalls(denoOpenSpy, 2) - } catch (error) { - console.error('Test failed:', error) - throw error // This will cause the test to fail - } - } - - await setupTest() - await upgradeTest() -}) diff --git a/src/services/downloader.ts b/src/services/downloader.ts deleted file mode 100644 index 85ae7e5..0000000 --- a/src/services/downloader.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { join } from 'https://deno.land/std@0.133.0/path/mod.ts' -import { SupportedOs } from '../types/platforms.ts' -import { darwinUnpack, unzip } from './unpacker.ts' -import osUtils from '../utils/os.ts' - -export class Downloader { - private os: string - private arch: string - private urlMap: Record - constructor() { - this.os = Deno.build.os // 'linux', 'darwin', or 'windows' - this.arch = Deno.build.arch // 'x86_64', 'arm64', etc. - - this.urlMap = { - [SupportedOs.Linux]: - 'https://releases.stackql.io/stackql/latest/stackql_linux_amd64.zip', - [SupportedOs.Windows]: - 'https://releases.stackql.io/stackql/latest/stackql_windows_amd64.zip', - [SupportedOs.Darwin]: - 'https://storage.googleapis.com/stackql-public-releases/latest/stackql_darwin_multiarch.pkg', - // Additional OS-architecture combinations can be added here - } - } - - private async downloadFile(url: string, downloadDir: string) { - const res = await fetch(url) - // create dir if not exists - - const file = await Deno.open(downloadDir, { create: true, write: true }) - - try { - await res.body?.pipeTo(file.writable).finally( - () => file.close(), //TODO: fix bad resource id when closing file - ) - } catch (error) { - console.error(`ERROR: [downloadFile] ${error.message}`) - } - - console.log('Closed file') - } - - private getUrl(): string { - const key = `${this.os}` - const url = this.urlMap[key] - - if (!url) { - throw new Error(`Unsupported OS type: ${this.os}`) - } - - return url - } - - /** - * Gets binary name - * @returns binrary name - */ - private getBinaryName() { - const binaryMap: Record = { - [SupportedOs.Windows]: 'stackql.exe', - [SupportedOs.Darwin]: 'stackql/Payload/stackql', - [SupportedOs.Linux]: 'stackql', // Default case for Linux and other platforms - } - const os = Deno.build.os.toLowerCase() - - if (!Object.values(SupportedOs).includes(os as SupportedOs)) { - throw new Error(`Unsupported OS type: ${os}`) - } - - const binaryOs = os as SupportedOs - return binaryMap[binaryOs] - } - - private async createDownloadDir(downloadDir: string) { - try { - const stat = await Deno.stat(downloadDir) - if (!stat.isDirectory) { - await Deno.mkdir(downloadDir, { recursive: true }) - } - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - await Deno.mkdir(downloadDir, { recursive: true }) - } else { - throw error - } - } - } - /** - * Gets download dir - * @returns download dir - */ - private getDownloadDir(): string { - const projectDir = Deno.cwd() - - if (!projectDir) { - throw new Error('Unable to determine the project directory.') - } - - const downloadDir = join(projectDir, '.stackql') - - return downloadDir - } - - private binaryExists(binaryName: string, downloadDir: string): boolean { - const binPath = join(downloadDir, binaryName) - try { - Deno.statSync(binPath) - return true - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - return false - } - throw error - } - } - private async installStackQL(downloadDir: string) { - const url = this.getUrl() - - const archiveFileName = `${downloadDir}/${url.split('/').pop()}` - await this.downloadFile(url, archiveFileName) - - console.log('Unpacking stackql binary') - const unpacker = Deno.build.os === 'darwin' ? darwinUnpack : unzip - await unpacker({ downloadDir, archiveFileName }) - } - - private async setExecutable(binaryPath: string) { - const allowExecOctal = 0o755 - await osUtils.chomod(binaryPath, allowExecOctal) - } - - private async downloadAndInstallStackQL({ - downloadDir, - binaryName, - }: { - downloadDir: string - binaryName: string - }) { - const binaryPath = join(downloadDir, binaryName) - await this.installStackQL(downloadDir) - await this.setExecutable(binaryPath) - return binaryPath - } - /** - * Setup stackql binary, check if binary exists, if not download it - */ - public async setupStackQL() { - try { - const binaryName = this.getBinaryName() - const downloadDir = this.getDownloadDir() - await this.createDownloadDir(downloadDir) - - let binaryPath = join(downloadDir, binaryName) - - if (this.binaryExists(binaryName, downloadDir)) { - await this.setExecutable(binaryPath) - return binaryPath - } - - binaryPath = await this.downloadAndInstallStackQL({ - downloadDir, - binaryName, - }) - return binaryPath - } catch (error) { - console.error(`ERROR: [setup] ${error.message}`) - Deno.exit(1) - } - } - - private async removeStackQL() { - const downloadDir = this.getDownloadDir() - await Deno.remove(join(downloadDir, '/'), { recursive: true }) - console.log('stackql download dir removed') - } - - public async upgradeStackQL() { - if (Deno.build.os === 'darwin') { - await this.removeStackQL() - } - const binaryName = this.getBinaryName() - const downloadDir = this.getDownloadDir() - await this.createDownloadDir(downloadDir) - const binaryPath = await this.downloadAndInstallStackQL({ - downloadDir, - binaryName, - }) - return binaryPath - } -} diff --git a/src/services/server.test.ts b/src/services/server.test.ts deleted file mode 100644 index 74fe8b5..0000000 --- a/src/services/server.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Server } from './server.ts' -import { assert } from 'https://deno.land/std@0.207.0/assert/assert.ts' -import { startStackQLServer } from '../../testing/utils.ts' - -Deno.test('Successful Connection', async () => { - const { closeProcess } = await startStackQLServer() - const server = new Server() - const pg = await server.connect( - 'postgres://postgres:password@localhost:5444/postgres', - ) - assert(pg) - await server.close() - await closeProcess() -}) diff --git a/src/services/server.ts b/src/services/server.ts deleted file mode 100644 index 2a64961..0000000 --- a/src/services/server.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Client } from 'https://deno.land/x/postgres/mod.ts' - -export class Server { - private client: Client | null = null - - constructor(connectionString?: string) { - if (connectionString) { - this.client = new Client(connectionString) - } - } - - public async connect(connectionString?: string) { - const maxRetries = 3 - let currentAttempt = 0 - - while (currentAttempt < maxRetries) { - try { - const connection = Deno.env.get('POSTGRES') || connectionString - if (!connection) { - throw new Error( - 'Connection string not found \n Please set the POSTGRES environment variable or pass the connection string as an argument', - ) - } - - console.log('connecting', connection) - this.client = new Client(connection) - await this.client.connect() - console.log('connected') - return this.client - } catch (error) { - currentAttempt++ - console.log( - `Attempt ${currentAttempt} failed: ${error.message}`, - ) - - if (currentAttempt >= maxRetries) { - throw new Error( - `Could not connect to the server after ${maxRetries} attempts: ${error.message}`, - ) - } - - // Wait for 1 second before the next attempt - await new Promise((resolve) => setTimeout(resolve, 1000)) - } - } - } - - public async close() { - if (this.client) { - await this.client.end() - } - } - - // Additional methods for query execution can be added here -} diff --git a/src/services/unpacker.ts b/src/services/unpacker.ts deleted file mode 100644 index e93bc2f..0000000 --- a/src/services/unpacker.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { decompress } from 'https://deno.land/x/zip/mod.ts' - -type UnpackParams = { - downloadDir: string - archiveFileName: string -} - -export const darwinUnpack = async (params: UnpackParams) => { - console.log('darwinUnpack') - const { downloadDir, archiveFileName } = params - const unpackedFileName = `${downloadDir}/stackql` - const commandPath = 'pkgutil' - const commandArgs = ['--expand-full', archiveFileName, unpackedFileName] - const process = new Deno.Command(commandPath, { - args: commandArgs, - stdout: 'piped', - stderr: 'piped', - }) - - const { code, stdout, stderr } = await process.output() - if (code !== 0) { - const output = new TextDecoder().decode(stdout) - const errorOutput = new TextDecoder().decode(stderr) - console.error('Error executing pkgutil:', output, errorOutput) - throw new Error('Failed to unpack stackql') - } -} - -export const unzip = async (params: UnpackParams) => { - console.log('unzip') - try { - await decompress(params.archiveFileName, params.downloadDir) - } catch (error) { - console.log('[unzip] error:', error) - throw new Error('Failed to unpack stackql') - } -} diff --git a/src/stackql.test.ts b/src/stackql.test.ts deleted file mode 100644 index 2762543..0000000 --- a/src/stackql.test.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { assertStringIncludes } from 'https://deno.land/std@0.206.0/assert/mod.ts' -import { StackQL } from './stackql.ts' -import { - isCsvString, - isJsonString, - startStackQLServer, -} from '../testing/utils.ts' -import { - assertEquals, - assertExists, -} from 'https://deno.land/std@0.160.0/testing/asserts.ts' -import { Downloader } from './services/downloader.ts' -import { - assertSpyCall, - spy, -} from 'https://deno.land/std@0.207.0/testing/mock.ts' -import osUtils from './utils/os.ts' -import { assert } from 'https://deno.land/std@0.133.0/_util/assert.ts' - -const downloader = new Downloader() - -const setupStackQL = async () => { - await downloader.setupStackQL() -} - -Deno.test('StackQL CLI run query', async () => { - await setupStackQL() - - // Arrange - const stackQL = new StackQL() - await stackQL.initialize({ serverMode: false }) - - const pullQuery = 'REGISTRY PULL github;' - const providerQuery = 'SHOW PROVIDERS' - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'` - - // Act - await stackQL.runQuery(pullQuery) - const result = await stackQL.runQuery(providerQuery) - const githubResult = await stackQL.runQuery(githubTestQuery) - - // Assert - assertStringIncludes(result, 'name') - assertStringIncludes(result, 'version') - assertStringIncludes(result, 'github') - assertStringIncludes(githubResult, 'stackql') -}) - -Deno.test('Set properties from configs', async () => { - await setupStackQL() - const runCliSpy = spy(osUtils, 'runCommand') - const stackQL = new StackQL() - await stackQL.initialize({ - serverMode: false, - maxResults: 100, - pageLimit: 10, - maxDepth: 5, - apiTimeout: 5000, - }) - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'` - - await stackQL.runQuery(githubTestQuery) - - const params = stackQL.getParams() - assertEquals(params.length, 8) - assertEquals(params, [ - '--http.response.maxResults', - '100', - '--http.response.pageLimit', - '10', - '--indirect.depth.max', - '5', - '--apirequesttimeout', - '5000', - ]) - const binaryPath = stackQL.getBinaryPath() - assert(binaryPath) - assertSpyCall(runCliSpy, 0, { - args: [binaryPath, ['exec', githubTestQuery, ...params]], - }) - runCliSpy.restore() -}) - -Deno.test('Set proxy properties from configs', async () => { - await setupStackQL() - const runCommandSpy = spy(osUtils, 'runCommand') - const stackQL = new StackQL() - await stackQL.initialize({ - serverMode: false, - proxyHost: 'localhost', - proxyPort: 8080, - proxyUser: 'user', - proxyPassword: 'password', - proxyScheme: 'https', - }) - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'` - - await stackQL.runQuery(githubTestQuery) - - const params = stackQL.getParams() - assertEquals(params, [ - '--http.proxy.host', - 'localhost', - '--http.proxy.port', - '8080', - '--http.proxy.user', - 'user', - '--http.proxy.password', - 'password', - '--http.proxy.scheme', - 'https', - ]) - const binaryPath = stackQL.getBinaryPath() - assert(binaryPath) - assertSpyCall(runCommandSpy, 0, { - args: [binaryPath, ['exec', githubTestQuery, ...params]], - }) - runCommandSpy.restore() -}) - -Deno.test('run query Output: json', async () => { - await setupStackQL() - const stackQL = new StackQL() - await stackQL.initialize({ - serverMode: false, - outputFormat: 'json', - }) - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'` - - const result = await stackQL.runQuery(githubTestQuery) - - const params = stackQL.getParams() - - assertEquals(params, [ - '--output', - 'json', - ]) - assert(isJsonString(result)) - assert(!(await isCsvString(result))) -}) - -Deno.test('run query Output: csv', async () => { - await setupStackQL() - const stackQL = new StackQL() - await stackQL.initialize({ - serverMode: false, - outputFormat: 'csv', - }) - const githubTestQuery = - `SELECT id, name from github.repos.repos where org='stackql'` - - const result = await stackQL.runQuery(githubTestQuery) - const params = stackQL.getParams() - - assertEquals(params, [ - '--output', - 'csv', - ]) - assert(!isJsonString(result)) - assert(await isCsvString(result)) -}) - -Deno.test('StackQL runServerQuery', async () => { - const { closeProcess } = await startStackQLServer() - const stackQL = new StackQL() - - try { - // Arrange - await stackQL.initialize({ - serverMode: true, - connectionString: - 'postgres://postgres:password@localhost:5444/postgres', - }) - const pullQuery = 'REGISTRY PULL github;' - const testQuery = 'SHOW SERVICES IN github LIKE \'%repos%\';' // Replace with a valid query for your context - - // Act - await stackQL.runServerQuery(pullQuery) - const results = await stackQL.runServerQuery(testQuery) - assertExists(results) - assertEquals(results.length, 1) - const result = results[0] as { - name: string - } - assertEquals(result.name, 'repos') - - // Assert - } finally { - // Cleanup - await closeProcess() - await stackQL.closeConnection() - } -}) - -Deno.test('getVersion', async () => { - await setupStackQL() - const stackQL = new StackQL() - await stackQL.initialize({ serverMode: false }) - const versionRegex = /^v?(\d+(?:\.\d+)*)$/ - const shaRegex = /^[a-f0-9]{7}$/ - - const { version, sha } = await stackQL.getVersion() - - assert(version) - assert(sha) - assert(versionRegex.test(version)) - assert(shaRegex.test(sha)) -}) - -Deno.test('getVersion when version and sha are undefined', async () => { - await setupStackQL() - const stackQL = new StackQL() - await stackQL.initialize({ serverMode: false }) - const versionRegex = /^v?(\d+(?:\.\d+)*)$/ - const shaRegex = /^[a-f0-9]{7}$/ // deno-lint-ignore no-explicit-any - ;(stackQL as any).version = undefined // deno-lint-ignore no-explicit-any - ;(stackQL as any).sha = undefined - // deno-lint-ignore no-explicit-any - assert((stackQL as any).version === undefined) - // deno-lint-ignore no-explicit-any - assert((stackQL as any).sha === undefined) - - const { version, sha } = await stackQL.getVersion() - - assert(version) - assert(sha) - assert(versionRegex.test(version)) - assert(shaRegex.test(sha)) -}) - -Deno.test('upgrade stackql', async () => { - await setupStackQL() - const stackQL = new StackQL() - await stackQL.initialize({ serverMode: false }) // deno-lint-ignore no-explicit-any - ;(stackQL as any).version = undefined // deno-lint-ignore no-explicit-any - ;(stackQL as any).sha = undefined - const versionRegex = /^v?(\d+(?:\.\d+)*)$/ - const shaRegex = /^[a-f0-9]{7}$/ - // deno-lint-ignore no-explicit-any - assert((stackQL as any).version === undefined) - // deno-lint-ignore no-explicit-any - assert((stackQL as any).sha === undefined) - - const { version, sha } = await stackQL.upgrade() - - assert(version) - assert(sha) - assert(versionRegex.test(version)) - assert(shaRegex.test(sha)) -}) diff --git a/src/stackql.ts b/src/stackql.ts deleted file mode 100644 index 981696e..0000000 --- a/src/stackql.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { assertExists } from 'https://deno.land/std@0.206.0/assert/assert_exists.ts' -import { Downloader } from './services/downloader.ts' -import osUtils from './utils/os.ts' -import { Server } from './services/server.ts' -import { Client } from 'https://deno.land/x/postgres@v0.17.0/client.ts' - -export interface StackQLConfig { - binaryPath?: string - serverMode?: boolean - connectionString?: string - maxResults?: number - pageLimit?: number - maxDepth?: number - apiTimeout?: number - proxyHost?: string - proxyPort?: number - proxyUser?: string - proxyPassword?: string - proxyScheme?: 'http' | 'https' - outputFormat?: 'csv' | 'json' -} - -export class StackQL { - private binaryPath?: string //The full path of the `stackql` executable (not supported in `server_mode`). - private downloader: Downloader = new Downloader() - private serverMode = false - private connection?: Client - private outputFormat: 'csv' | 'json' = 'json' - private params: string[] = [] - private version: string | undefined // The version number of the `stackql` executable (not supported in `server_mode`) - private sha: string | undefined // The commit (short) sha for the installed `stackql` binary build (not supported in `server_mode`). - constructor() { - } - - getParams() { - return this.params - } - - getBinaryPath() { - return this.binaryPath - } - - async getVersion() { - if (!this.version) { - await this.updateVersion() - } - - return { version: this.version, sha: this.sha } - } - - async execute(query: string) { - if (this.serverMode) { - const result = await this.runServerQuery(query) - } - return await this.runQuery(query) - } - - private async updateVersion() { - if (!this.binaryPath) { - throw new Error('Binary path not found') - } - const output = await osUtils.runCommand(this.binaryPath, ['--version']) - if (output) { - const versionTokens: string[] = output.split('\n')[0].split(' ') - const version: string = versionTokens[1] - const sha: string = versionTokens[3].replace('(', '').replace( - ')', - '', - ) - - this.version = version - this.sha = sha - } - } - - /** - * Upgrade the `stackql` executable to the latest version - * @returns The version number of the `stackql` executable (not supported in `server_mode`) - */ - async upgrade() { - this.binaryPath = await this.downloader.upgradeStackQL() - await this.updateVersion() - return this.getVersion() - } - - /** - * Initialize the `stackql` executable - * @param config The configuration object - * @returns The binary path of the `stackql` executable if `server_mode` is `false`, otherwise `undefined` - */ - public async initialize(config: StackQLConfig) { - this.binaryPath = config.binaryPath - this.serverMode = config.serverMode || false - if (this.serverMode) { - await this.setupConnection(config.connectionString) - return - } - if (this.binaryExist()) { - return - } - this.binaryPath = await this.downloader.setupStackQL() - this.setProperties(config) - } - - private binaryExist() { - return !!this.binaryPath && osUtils.fileExists(this.binaryPath) - } - private setProxyProperties(config: StackQLConfig): void { - if (config.proxyHost !== undefined) { - this.params.push('--http.proxy.host') - this.params.push(config.proxyHost) - } - - if (config.proxyPort !== undefined) { - this.params.push('--http.proxy.port') - this.params.push(config.proxyPort.toString()) - } - - if (config.proxyUser !== undefined) { - this.params.push('--http.proxy.user') - this.params.push(config.proxyUser) - } - - if (config.proxyPassword !== undefined) { - this.params.push('--http.proxy.password') - this.params.push(config.proxyPassword) - } - - if (config.proxyScheme !== undefined) { - if (!['http', 'https'].includes(config.proxyScheme)) { - throw new Error( - `Invalid proxyScheme. Expected one of ['http', 'https'], got ${config.proxyScheme}.`, - ) - } - this.params.push('--http.proxy.scheme') - this.params.push(config.proxyScheme) - } - } - - private setProperties(config: StackQLConfig): void { - if (config.maxResults !== undefined) { - this.params.push('--http.response.maxResults') - this.params.push(config.maxResults.toString()) - } - - if (config.pageLimit !== undefined) { - this.params.push('--http.response.pageLimit') - this.params.push(config.pageLimit.toString()) - } - - if (config.maxDepth !== undefined) { - this.params.push('--indirect.depth.max') - this.params.push(config.maxDepth.toString()) - } - - if (config.apiTimeout !== undefined) { - this.params.push('--apirequesttimeout') - this.params.push(config.apiTimeout.toString()) - } - - if (config.proxyHost !== undefined) { - this.setProxyProperties(config) - } - - if (config.outputFormat !== undefined) { - if (!['csv', 'json'].includes(config.outputFormat)) { - throw new Error( - `Invalid outputFormat. Expected one of ['csv', 'json'], got ${config.outputFormat}.`, - ) - } - this.params.push('--output') - this.params.push(config.outputFormat) - this.outputFormat = config.outputFormat - } - } - - /** - * Run a StackQL query - * @param query The StackQL query - * @returns The result of the query - */ - public async runQuery(query: string) { - assertExists(this.binaryPath) - const args = ['exec', query].concat(this.params) - try { - const result = await osUtils.runCommand(this.binaryPath, args) - return result - } catch (error) { - console.error(error) - throw new Error(`StackQL query failed: ${error.message}`) - } - } - - //////////////////////Server mode related methods - private async queryObjectFormat(query: string) { - assertExists(this.connection) - const pgResult = await this.connection.queryObject(query) - return pgResult.rows - } - - private async setupConnection(connectionString?: string) { - const server = new Server() - this.connection = await server.connect(connectionString) - } - - /** - * Close the connection to the stackql server - */ - public async closeConnection() { - if (this.connection) { - await this.connection.end() - } - } - - /** - * Run a StackQL query on the server - * @param query The StackQL query - * @returns The result of the query - */ - public async runServerQuery(query: string) { - try { - if (this.outputFormat === 'json') { - const result = await this.queryObjectFormat(query) - return result - } - } catch (error) { - console.error(error) - throw new Error(`StackQL server query failed: ${error.message}`) - } - } -} diff --git a/src/types/platforms.ts b/src/types/platforms.ts deleted file mode 100644 index 7688704..0000000 --- a/src/types/platforms.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum SupportedOs { - Linux = 'linux', - Darwin = 'darwin', - Windows = 'windows', -} diff --git a/src/utils/os.test.ts b/src/utils/os.test.ts deleted file mode 100644 index 98ebd24..0000000 --- a/src/utils/os.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - assertRejects, - assertStringIncludes, -} from 'https://deno.land/std@0.160.0/testing/asserts.ts' -import osUtils from './os.ts' - -Deno.test('osUtils.runCommand: Test Successful Execution', async () => { - const command = Deno.build.os === 'windows' ? 'cmd' : 'echo' - const args = Deno.build.os === 'windows' - ? ['/c', 'echo', 'Hello, World!'] - : ['Hello, World!'] - const result = await osUtils.runCommand(command, args) - assertStringIncludes(result.trim(), 'Hello, World!') -}) - -Deno.test('osUtils.runCommand: Test Failed Execution', () => { - const command = 'invalid' - const args = Deno.build.os === 'windows' - ? ['/c', 'echo', 'Hello, World!'] - : ['Hello, World!'] - assertRejects(async () => { - await osUtils.runCommand(command, args) - }) -}) diff --git a/src/utils/os.ts b/src/utils/os.ts deleted file mode 100644 index a0876ca..0000000 --- a/src/utils/os.ts +++ /dev/null @@ -1,48 +0,0 @@ -const fileExists = (path?: string) => { - if (!path) { - return false - } - try { - Deno.statSync(path) - return true - } catch (error) { - if (error instanceof Deno.errors.NotFound) { - return false - } - throw error - } -} - -const chomod = async (path: string, mode: number) => { - if (Deno.build.os !== 'windows') { - await Deno.chmod(path, mode) - } -} - -const runCommand = async (path: string, args: string[]) => { - const process = new Deno.Command(path, { - args, - stdout: 'piped', - stderr: 'piped', - }) - - const { code, stdout, stderr } = await process.output() - - if (code === 0) { - const output = stdout - const result = new TextDecoder().decode(output) - return result - } else { - const errorOutput = stderr - const errorMessage = new TextDecoder().decode(errorOutput) - throw new Error(errorMessage) - } -} - -const osUtils = { - fileExists, - chomod, - runCommand, -} - -export default osUtils diff --git a/testing/utils.ts b/testing/utils.ts deleted file mode 100644 index 02f98b0..0000000 --- a/testing/utils.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { existsSync } from "https://deno.land/std/fs/mod.ts"; -import { join } from "https://deno.land/std@0.133.0/path/mod.ts"; -import { parse } from "https://deno.land/std@0.134.0/encoding/csv.ts"; - -export const removeStackQLDownload = async () => { - const projectDir = Deno.cwd(); - const stackqlPath = join(projectDir, ".stackql"); - console.log("stackqlPath", stackqlPath); - console.log("existsSync(stackqlPath)", existsSync(stackqlPath)); - if (existsSync(stackqlPath)) { - console.log("Removing .stackql directory"); - await Deno.remove(stackqlPath, { recursive: true }); - } -}; - -export const startStackQLServer = async (port = 5444) => { - const command = new Deno.Command("stackql", { - args: [ - "srv", - "--pgsrv.address=0.0.0.0", - `--pgsrv.port=${port}`, - ], - stdout: "inherit", - stderr: "inherit", - }); - const process = command.spawn(); - //TODO: find a way to wait for the server to be ready - const closeProcess = async () => { - try { - console.log("Closing process"); - process.kill(); - await process.status; - } catch (error) { - const alreadyClosed = error.message.includes( - "Child process has already terminated", - ); - if (alreadyClosed) { - console.log("Process already closed"); - return; - } - throw error; - } - }; - return { closeProcess }; -}; - -export const isJsonString = (str: string) => { - try { - JSON.parse(str); - } catch (_error) { - return false; - } - return true; -}; - -export const isCsvString = async (str: string) => { - try { - await parse(str); - } catch (_error) { - return false; - } - return true; -};