From e2ae37b285af70ca04bf5849dced189affdcad13 Mon Sep 17 00:00:00 2001 From: James Clarke Date: Thu, 27 Jul 2023 22:48:34 +0100 Subject: [PATCH 1/2] Move rawParse/Execute methods to AdminFetchConn + add ability to provide AbortSignal --- packages/driver/src/baseConn.ts | 60 +---------------- packages/driver/src/fetchConn.ts | 93 +++++++++++++++++++++++++-- packages/driver/test/client.test.ts | 52 ++++----------- packages/driver/test/keys/jwt | 1 - packages/driver/test/keys/private.pem | 5 -- packages/driver/test/keys/public.pem | 4 -- packages/driver/test/testUtil.ts | 2 - 7 files changed, 100 insertions(+), 117 deletions(-) delete mode 100644 packages/driver/test/keys/jwt delete mode 100644 packages/driver/test/keys/private.pem delete mode 100644 packages/driver/test/keys/public.pem diff --git a/packages/driver/src/baseConn.ts b/packages/driver/src/baseConn.ts index 05e9d737f..70c36ee2a 100644 --- a/packages/driver/src/baseConn.ts +++ b/packages/driver/src/baseConn.ts @@ -78,19 +78,13 @@ new DataView(NO_TRANSACTION_CAPABILITIES_BYTES.buffer).setUint32( NO_TRANSACTION_CAPABILITIES ); -const RESTRICTED_CAPABILITIES = +export const RESTRICTED_CAPABILITIES = (Capabilities.ALL & ~Capabilities.TRANSACTION & ~Capabilities.SESSION_CONFIG & ~Capabilities.SET_GLOBAL) >>> 0; -const STUDIO_CAPABILITIES = - (RESTRICTED_CAPABILITIES | - Capabilities.SESSION_CONFIG | - Capabilities.SET_GLOBAL) >>> - 0; - enum CompilationFlag { INJECT_OUTPUT_TYPE_IDS = 1 << 0, INJECT_OUTPUT_TYPE_NAMES = 1 << 1, @@ -1038,7 +1032,7 @@ export class BaseRawConnection { ]; } - private async _executeFlow( + protected async _executeFlow( query: string, args: QueryArgs, outputFormat: OutputFormat, @@ -1447,54 +1441,4 @@ export class BaseRawConnection { async close(): Promise { this._abort(); } - - // These methods are exposed for use by EdgeDB Studio - public async rawParse( - query: string, - state: Session, - options?: QueryOptions - ): Promise< - [ICodec, ICodec, Uint8Array, Uint8Array, ProtocolVersion, number] - > { - const result = (await this._parse( - query, - OutputFormat.BINARY, - Cardinality.MANY, - state, - STUDIO_CAPABILITIES, - options - ))!; - return [ - result[1], - result[2], - result[4]!, - result[5]!, - this.protocolVersion, - result[3], - ]; - } - - public async rawExecute( - query: string, - state: Session, - outCodec?: ICodec, - options?: QueryOptions, - inCodec?: ICodec, - args: QueryArgs = null - ): Promise { - const result = new WriteBuffer(); - await this._executeFlow( - query, - args, - outCodec ? OutputFormat.BINARY : OutputFormat.NONE, - Cardinality.MANY, - state, - inCodec ?? NULL_CODEC, - outCodec ?? NULL_CODEC, - result, - STUDIO_CAPABILITIES, - options - ); - return result.unwrap(); - } } diff --git a/packages/driver/src/fetchConn.ts b/packages/driver/src/fetchConn.ts index 827ff8a72..d6b93eab8 100644 --- a/packages/driver/src/fetchConn.ts +++ b/packages/driver/src/fetchConn.ts @@ -16,13 +16,29 @@ * limitations under the License. */ +import { + BaseRawConnection, + Capabilities, + PROTO_VER, + RESTRICTED_CAPABILITIES, +} from "./baseConn"; +import { NULL_CODEC } from "./codecs/codecs"; +import { ICodec } from "./codecs/ifaces"; import { CodecsRegistry } from "./codecs/registry"; import { Address, NormalizedConnectConfig } from "./conUtils"; -import { PROTO_VER, BaseRawConnection } from "./baseConn"; -import Event from "./primitives/event"; -import * as chars from "./primitives/chars"; import { InternalClientError, ProtocolError } from "./errors"; import type { HttpSCRAMAuth } from "./httpScram"; +import { + Cardinality, + OutputFormat, + ProtocolVersion, + QueryArgs, + QueryOptions, +} from "./ifaces"; +import { Session } from "./options"; +import { WriteBuffer } from "./primitives/buffer"; +import * as chars from "./primitives/chars"; +import Event from "./primitives/event"; interface FetchConfig { address: Address | string; @@ -34,9 +50,16 @@ interface FetchConfig { const PROTO_MIME = `application/x.edgedb.v_${PROTO_VER[0]}_${PROTO_VER[1]}.binary'`; +const STUDIO_CAPABILITIES = + (RESTRICTED_CAPABILITIES | + Capabilities.SESSION_CONFIG | + Capabilities.SET_GLOBAL) >>> + 0; + class BaseFetchConnection extends BaseRawConnection { protected config: FetchConfig; protected addr: string; + protected abortSignal: AbortSignal | null = null; constructor(config: FetchConfig, registry: CodecsRegistry) { super(registry); @@ -96,6 +119,7 @@ class BaseFetchConnection extends BaseRawConnection { method: "post", body: data, headers, + signal: this.abortSignal, }); if (!resp.ok) { @@ -129,16 +153,17 @@ class BaseFetchConnection extends BaseRawConnection { this.__sendData(data); } - static create( + static create( + this: T, config: FetchConfig, registry: CodecsRegistry - ): BaseFetchConnection { + ): InstanceType { const conn = new this(config, registry); conn.connected = true; conn.connWaiter.set(); - return conn; + return conn as InstanceType; } } @@ -158,6 +183,62 @@ export class AdminUIFetchConnection extends BaseFetchConnection { const baseUrl = `${protocol}://${address[0]}:${address[1]}`; return `${baseUrl}/db/${database}`; } + + // These methods are exposed for use by EdgeDB Studio + public async rawParse( + query: string, + state: Session, + options?: QueryOptions, + abortSignal?: AbortSignal | null + ): Promise< + [ICodec, ICodec, Uint8Array, Uint8Array, ProtocolVersion, number] + > { + this.abortSignal = abortSignal ?? null; + + const result = (await this._parse( + query, + OutputFormat.BINARY, + Cardinality.MANY, + state, + STUDIO_CAPABILITIES, + options + ))!; + return [ + result[1], + result[2], + result[4]!, + result[5]!, + this.protocolVersion, + result[3], + ]; + } + + public async rawExecute( + query: string, + state: Session, + outCodec?: ICodec, + options?: QueryOptions, + inCodec?: ICodec, + args: QueryArgs = null, + abortSignal?: AbortSignal | null + ): Promise { + this.abortSignal = abortSignal ?? null; + + const result = new WriteBuffer(); + await this._executeFlow( + query, + args, + outCodec ? OutputFormat.BINARY : OutputFormat.NONE, + Cardinality.MANY, + state, + inCodec ?? NULL_CODEC, + outCodec ?? NULL_CODEC, + result, + STUDIO_CAPABILITIES, + options + ); + return result.unwrap(); + } } const _tokens = new WeakMap(); diff --git a/packages/driver/test/client.test.ts b/packages/driver/test/client.test.ts index 56fc36d7f..66734ba07 100644 --- a/packages/driver/test/client.test.ts +++ b/packages/driver/test/client.test.ts @@ -40,8 +40,6 @@ import { InvalidReferenceError, } from "../src/index.node"; -import { retryingConnect } from "../src/retry"; -import { RawConnection } from "../src/rawConn"; import { AdminUIFetchConnection } from "../src/fetchConn"; import { CustomCodecSpec } from "../src/codecs/registry"; import { @@ -52,6 +50,7 @@ import { isDeno, } from "./testbase"; import { PG_VECTOR_MAX_DIM } from "../src/codecs/pgvector"; +import { HTTPSCRAMAuth } from "../src/httpScram"; function setCustomCodecs(codecs: (keyof CustomCodecSpec)[], client: Client) { // @ts-ignore @@ -2041,53 +2040,24 @@ function _decodeResultBuffer(outCodec: _ICodec, resultData: Uint8Array) { return result; } -// 'raw' methods are only used by edgedb studio, so only need to work with -// EdgeDB 2.0 or greater -if (getEdgeDBVersion().major >= 2) { - test("'implicit*' headers", async () => { - const config = await parseConnectArguments(getConnectOptions()); - const registry = new _CodecsRegistry(); - const con = await retryingConnect( - RawConnection.connectWithTimeout.bind(RawConnection), - config, - registry - ); - try { - const query = `SELECT Function { - name - }`; - const state = new Session({ module: "schema" }); - const options = { - injectTypenames: true, - implicitLimit: BigInt(5), - } as const; - const [_, outCodec] = await con.rawParse(query, state, options); - const resultData = await con.rawExecute(query, state, outCodec, options); - - const result = _decodeResultBuffer(outCodec, resultData); - - expect(result).toHaveLength(5); - expect(result[0]["__tname__"]).toBe("schema::Function"); - } finally { - await con.close(); - } - }); -} - if (!isDeno && getAvailableFeatures().has("binary-over-http")) { - // @ts-ignore // make deno ignore skip - test.skip("binary protocol over http", async () => { - //@ts-ignore - const tokenFile = require("path").join(__dirname, "keys", "jwt"); - //@ts-ignore - const token = require("fs").readFileSync(tokenFile, "utf8").trim(); + test("binary protocol over http", async () => { const codecsRegistry = new _CodecsRegistry(); const config = await parseConnectArguments(getConnectOptions()); + + const { address, user, password } = config.connectionParams; + const token = await HTTPSCRAMAuth( + `http://${address[0]}:${address[1]}`, + user, + password! + ); + const fetchConn = AdminUIFetchConnection.create( { address: config.connectionParams.address, database: config.connectionParams.database, user: config.connectionParams.user, + tlsSecurity: "insecure", token: token, }, codecsRegistry diff --git a/packages/driver/test/keys/jwt b/packages/driver/test/keys/jwt deleted file mode 100644 index 7e91d91ae..000000000 --- a/packages/driver/test/keys/jwt +++ /dev/null @@ -1 +0,0 @@ -eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6InhUbk9ObFN3NnlWczZQSGVsU01WZ2tuUVB2VVZob0dDRk5yMzZReDVuajgiLCJ5Ijoid2lfLTBIVF9fbEdKVF8tdFhra2hjNHFfUEZvUFpoR2xwcFMwTU9NX21mdyJ9fQ..z3gM9GcM_uzk8w-n.2wN9bdFRECrgO0RNPb6-DEP4APy7jwHBK-cl8Ry3EqN5WG3u1VQ6Jm5RkeGZk2B9xCDEFSaglEEIEaXn-_CcQ3DvGZYzhvwDdJa4jJQzdBc4P5yA2ZP_0K0VRLl6zaq29WiFeeRDeU9pIjdZy0kmurgxB__n1eQcVjxE-y_NwyHP_h8SuBEUey2Yyl-bdJCyJ8JOSeKJ.Bk0XpWkl_VHOwsZEX1s0Og diff --git a/packages/driver/test/keys/private.pem b/packages/driver/test/keys/private.pem deleted file mode 100644 index d5156fe69..000000000 --- a/packages/driver/test/keys/private.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEINz/wS2W5vcJcOCQVCfHnKfpByta9eDXpYMpdllnGS5moAoGCCqGSM49 -AwEHoUQDQgAELJAGmt2ZqKPLi5NlaX3N2eruyqlqklRNCfvJGOCARTQsd/RPrhwu -G0qiRK33ILY0+ciIbM1pCqlcfOiV7oUjBA== ------END EC PRIVATE KEY----- diff --git a/packages/driver/test/keys/public.pem b/packages/driver/test/keys/public.pem deleted file mode 100644 index 74051f0af..000000000 --- a/packages/driver/test/keys/public.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELJAGmt2ZqKPLi5NlaX3N2eruyqlq -klRNCfvJGOCARTQsd/RPrhwuG0qiRK33ILY0+ciIbM1pCqlcfOiV7oUjBA== ------END PUBLIC KEY----- diff --git a/packages/driver/test/testUtil.ts b/packages/driver/test/testUtil.ts index 33572ce63..d9bcefac9 100644 --- a/packages/driver/test/testUtil.ts +++ b/packages/driver/test/testUtil.ts @@ -113,8 +113,6 @@ export const getServerCommand = ( if (help.includes("--admin-ui")) { args.push("--http-endpoint-security=optional"); - // args.push("--jws-key-file", path.join(__dirname, "keys", "public.pem")); - // args.push("--jwe-key-file", path.join(__dirname, "keys", "private.pem")); args.push("--jose-key-mode=generate"); availableFeatures.push("binary-over-http"); From 846a8dd7e9927b04d4728c949e36a7018e774623 Mon Sep 17 00:00:00 2001 From: James Clarke Date: Thu, 14 Sep 2023 17:27:21 +0100 Subject: [PATCH 2/2] Fix test --- packages/driver/test/client.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/driver/test/client.test.ts b/packages/driver/test/client.test.ts index 66734ba07..351f26ee8 100644 --- a/packages/driver/test/client.test.ts +++ b/packages/driver/test/client.test.ts @@ -50,7 +50,8 @@ import { isDeno, } from "./testbase"; import { PG_VECTOR_MAX_DIM } from "../src/codecs/pgvector"; -import { HTTPSCRAMAuth } from "../src/httpScram"; +import { getHTTPSCRAMAuth } from "../src/httpScram"; +import cryptoUtils from "../src/adapter.crypto.node"; function setCustomCodecs(codecs: (keyof CustomCodecSpec)[], client: Client) { // @ts-ignore @@ -2046,7 +2047,7 @@ if (!isDeno && getAvailableFeatures().has("binary-over-http")) { const config = await parseConnectArguments(getConnectOptions()); const { address, user, password } = config.connectionParams; - const token = await HTTPSCRAMAuth( + const token = await getHTTPSCRAMAuth(cryptoUtils)( `http://${address[0]}:${address[1]}`, user, password!