From 7ddf063984f2fd39acf7bc55eecbf7a315c030bf Mon Sep 17 00:00:00 2001 From: Kamil Kisiela Date: Thu, 11 Apr 2024 09:00:31 +0200 Subject: [PATCH] CDN endpoints with and without /artifact-type part (#4484) --- .changeset/tough-brooms-compare.md | 5 +++++ packages/libraries/client/src/apollo.ts | 11 +++++++---- packages/libraries/client/src/gateways.ts | 19 ++++++++++++------- .../libraries/client/src/internal/utils.ts | 9 +++++++++ packages/libraries/router/CHANGELOG.md | 4 ++++ packages/libraries/router/src/registry.rs | 18 ++++++++++++++---- 6 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 .changeset/tough-brooms-compare.md diff --git a/.changeset/tough-brooms-compare.md b/.changeset/tough-brooms-compare.md new file mode 100644 index 000000000..a721b8487 --- /dev/null +++ b/.changeset/tough-brooms-compare.md @@ -0,0 +1,5 @@ +--- +"@graphql-hive/client": minor +--- + +Accepts now CDN endpoint without /services /supergraph etc subdirectories diff --git a/packages/libraries/client/src/apollo.ts b/packages/libraries/client/src/apollo.ts index 1ed325ba6..ff7ed33eb 100644 --- a/packages/libraries/client/src/apollo.ts +++ b/packages/libraries/client/src/apollo.ts @@ -8,21 +8,24 @@ import type { HivePluginOptions, SupergraphSDLFetcherOptions, } from './internal/types.js'; -import { isHiveClient } from './internal/utils.js'; +import { isHiveClient, joinUrl } from './internal/utils.js'; import { version } from './version.js'; -export function createSupergraphSDLFetcher({ endpoint, key }: SupergraphSDLFetcherOptions) { +export function createSupergraphSDLFetcher(options: SupergraphSDLFetcherOptions) { let cacheETag: string | null = null; let cached: { id: string; supergraphSdl: string; } | null = null; + const endpoint = options.endpoint.endsWith('/supergraph') + ? options.endpoint + : joinUrl(options.endpoint, 'supergraph'); return function supergraphSDLFetcher() { const headers: { [key: string]: string; } = { - 'X-Hive-CDN-Key': key, + 'X-Hive-CDN-Key': options.key, 'User-Agent': `hive-client/${version}`, }; @@ -44,7 +47,7 @@ export function createSupergraphSDLFetcher({ endpoint, key }: SupergraphSDLFetch const fetchWithRetry = (): Promise<{ id: string; supergraphSdl: string }> => { return axios - .get(endpoint + '/supergraph', { + .get(endpoint, { headers, }) .then(response => { diff --git a/packages/libraries/client/src/gateways.ts b/packages/libraries/client/src/gateways.ts index 604606da2..95fb06c3b 100644 --- a/packages/libraries/client/src/gateways.ts +++ b/packages/libraries/client/src/gateways.ts @@ -1,6 +1,7 @@ import { createHash } from 'node:crypto'; import axios from 'axios'; import type { SchemaFetcherOptions, ServicesFetcherOptions } from './internal/types.js'; +import { joinUrl } from './internal/utils.js'; import { version } from './version.js'; interface Schema { @@ -9,18 +10,22 @@ interface Schema { name: string; } -function createFetcher({ endpoint, key }: SchemaFetcherOptions & ServicesFetcherOptions) { +function createFetcher(options: SchemaFetcherOptions & ServicesFetcherOptions) { let cacheETag: string | null = null; let cached: { id: string; supergraphSdl: string; } | null = null; + const endpoint = options.endpoint.endsWith('/services') + ? options.endpoint + : joinUrl(options.endpoint, 'services'); + return function fetcher(): Promise { const headers: { [key: string]: string; } = { - 'X-Hive-CDN-Key': key, + 'X-Hive-CDN-Key': options.key, accept: 'application/json', 'User-Agent': `hive-client/${version}`, }; @@ -43,7 +48,7 @@ function createFetcher({ endpoint, key }: SchemaFetcherOptions & ServicesFetcher const fetchWithRetry = (): Promise => { return axios - .get(endpoint + '/services', { + .get(endpoint, { headers, responseType: 'json', }) @@ -81,8 +86,8 @@ function createFetcher({ endpoint, key }: SchemaFetcherOptions & ServicesFetcher }; } -export function createSchemaFetcher({ endpoint, key }: SchemaFetcherOptions) { - const fetcher = createFetcher({ endpoint, key }); +export function createSchemaFetcher(options: SchemaFetcherOptions) { + const fetcher = createFetcher(options); return function schemaFetcher() { return fetcher().then(schema => { @@ -108,8 +113,8 @@ export function createSchemaFetcher({ endpoint, key }: SchemaFetcherOptions) { }; } -export function createServicesFetcher({ endpoint, key }: ServicesFetcherOptions) { - const fetcher = createFetcher({ endpoint, key }); +export function createServicesFetcher(options: ServicesFetcherOptions) { + const fetcher = createFetcher(options); return function schemaFetcher() { return fetcher().then(services => { diff --git a/packages/libraries/client/src/internal/utils.ts b/packages/libraries/client/src/internal/utils.ts index 3faa2a293..a2ac01972 100644 --- a/packages/libraries/client/src/internal/utils.ts +++ b/packages/libraries/client/src/internal/utils.ts @@ -129,3 +129,12 @@ export function logIf(condition: boolean, message: string, logFn: (message: stri logFn(message); } } + +export function joinUrl(url: string, subdirectory: string) { + const normalizedUrl = url.endsWith('/') ? url.slice(0, -1) : url; + const normalizedSubdirectory = subdirectory.startsWith('/') + ? subdirectory.slice(1) + : subdirectory; + + return normalizedUrl + '/' + normalizedSubdirectory; +} diff --git a/packages/libraries/router/CHANGELOG.md b/packages/libraries/router/CHANGELOG.md index 22358c69f..c7aafd1d6 100644 --- a/packages/libraries/router/CHANGELOG.md +++ b/packages/libraries/router/CHANGELOG.md @@ -1,3 +1,7 @@ +# 10.04.2024 + +- `HIVE_CDN_ENDPOINT` and `endpoint` accept an URL with and without the `/supergraph` part + # 09.01.2024 - Introduce `HIVE_CDN_SCHEMA_FILE_PATH` environment variable to specify where to download the supergraph schema (default is `./supergraph-schema.graphql`) diff --git a/packages/libraries/router/src/registry.rs b/packages/libraries/router/src/registry.rs index 49de1a368..af8c387ca 100644 --- a/packages/libraries/router/src/registry.rs +++ b/packages/libraries/router/src/registry.rs @@ -33,7 +33,7 @@ impl HiveRegistry { key: None, poll_interval: None, accept_invalid_certs: Some(true), - schema_file_path: None + schema_file_path: None, }; // Pass values from user's config @@ -86,7 +86,7 @@ impl HiveRegistry { } // Resolve values - let endpoint = config.endpoint.unwrap_or_else(|| "".to_string()); + let mut endpoint = config.endpoint.unwrap_or_else(|| "".to_string()); let key = config.key.unwrap_or_else(|| "".to_string()); let poll_interval: u64 = match config.poll_interval { Some(value) => value, @@ -110,6 +110,14 @@ impl HiveRegistry { return Err(anyhow!("environment variable HIVE_CDN_ENDPOINT not found",)); } + if !endpoint.ends_with("/supergraph") { + if endpoint.ends_with("/") { + endpoint.push_str("supergraph") + } else { + endpoint.push_str("/supergraph") + } + } + // Throw if key is empty if key.is_empty() { return Err(anyhow!("environment variable HIVE_CDN_KEY not found")); @@ -118,7 +126,9 @@ impl HiveRegistry { // A hacky way to force the router to use GraphQL Hive CDN as the source of schema. // Our plugin does the polling and saves the supergraph to a file. // It also enables hot-reloading to makes sure Apollo Router watches the file. - let file_name = config.schema_file_path.unwrap_or("supergraph-schema.graphql".to_string()); + let file_name = config + .schema_file_path + .unwrap_or("supergraph-schema.graphql".to_string()); env::set_var("APOLLO_ROUTER_SUPERGRAPH_PATH", file_name.clone()); env::set_var("APOLLO_ROUTER_HOT_RELOAD", "true"); @@ -172,7 +182,7 @@ impl HiveRegistry { } let resp = client - .get(format!("{}/supergraph", self.endpoint)) + .get(self.endpoint.as_str()) .headers(headers) .send() .map_err(|e| e.to_string())?;