From 178f1a4f9c68fa0d2af59498b03f855d3329dbfe Mon Sep 17 00:00:00 2001 From: fauna-chase Date: Thu, 28 Sep 2023 17:58:34 -0500 Subject: [PATCH] parse nested endpoint structure in root config ENG-5544 --- src/lib/config/index.ts | 4 +++ src/lib/config/root-config.ts | 53 +++++++++++++++++++++++++++++++---- test/lib/config.test.ts | 37 ++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/lib/config/index.ts b/src/lib/config/index.ts index 76056802..ab4b4c10 100644 --- a/src/lib/config/index.ts +++ b/src/lib/config/index.ts @@ -35,6 +35,10 @@ export class Config { } } + keys(): Array { + return Object.keys(this.config); + } + numberOpt(key: string): number | undefined { const v = this.config[key]; if (v === undefined || typeof v === "number") { diff --git a/src/lib/config/root-config.ts b/src/lib/config/root-config.ts index 90ef0aaf..a6651fd9 100644 --- a/src/lib/config/root-config.ts +++ b/src/lib/config/root-config.ts @@ -7,11 +7,29 @@ export class RootConfig { constructor(config: Config) { this.defaultEndpoint = config.strOpt("default"); - this.endpoints = Object.fromEntries( - config - .allObjectsWhere((k) => k !== "default") - .map(([k, v]) => [k, Endpoint.fromConfig(v)]) - ); + + /** + * Our updated config uses endpoint. to reserve the endpoint namespace. + * It is possible prior users still have legacy config that has the endpoints at the top level. + * When config is updated, we will write the entire file, so it will either + * all be nested under endpoint or all legacy. The only time this wouldn't be + * the case is if a user manually modifies their file to have a top level + * endpoint after we have nested them all under endpoint. In that scenario, + * the added endpoint will not be recognized. + */ + if (RootConfig.configContainsNestedEndpointStructure(config)) { + this.endpoints = Object.fromEntries( + config + .objectsIn("endpoint") + .map(([k, v]) => [k, Endpoint.fromConfig(v)]) + ); + } else { + this.endpoints = Object.fromEntries( + config + .allObjectsWhere((k) => k !== "default") + .map(([k, v]) => [k, Endpoint.fromConfig(v)]) + ); + } if (this.defaultEndpoint === "default") { throw new InvalidConfigError( @@ -26,6 +44,31 @@ export class RootConfig { ); } } + + /** + * If there is an endpoint object in the config, and it has a + * object underneath it, we are saying that it uses the + * + * [endpoint.myacct] + * secret=*** + * + * structure. This allows us to support a legacy config that has + * a top level endpoint named endpoint. Once we rewrite the config + * to be endpoint object based adding one manually like that + * will not be recognized. + */ + private static configContainsNestedEndpointStructure( + config: Config + ): boolean { + if (config.objectExists("endpoint")) { + const endpointObj = config.object("endpoint"); + return endpointObj.keys().some((key) => { + return endpointObj.objectExists(key); + }); + } else { + return false; + } + } } /** diff --git a/test/lib/config.test.ts b/test/lib/config.test.ts index 4245cbf9..22d4e6b5 100644 --- a/test/lib/config.test.ts +++ b/test/lib/config.test.ts @@ -68,6 +68,43 @@ describe("root config", () => { url: "http://localhost:8443", }); }); + + it("supports config with endpoint nested", () => { + expect( + lookupEndpoint({ + rootConfig: { + default: "other-endpoint", + endpoint: { + "my-endpoint": { + secret: "fn1234", + }, + "other-endpoint": { + secret: "fn5678", + }, + }, + }, + }) + ).to.deep.contain({ + secret: "fn5678", + url: "https://db.fauna.com", + }); + }); + + it("supports legacy top level endpoint", () => { + expect( + lookupEndpoint({ + rootConfig: { + default: "endpoint", + endpoint: { + secret: "fn1234", + }, + }, + }) + ).to.deep.contain({ + secret: "fn1234", + url: "https://db.fauna.com", + }); + }); }); describe("root config with flags", () => {