Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .changeset/busy-cloths-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
'hive-console-sdk-rs': minor
---

Breaking Changes to avoid future breaking changes;

Switch to [Builder](https://rust-unofficial.github.io/patterns/patterns/creational/builder.html) pattern for `SupergraphFetcher`, `PersistedDocumentsManager` and `UsageAgent` structs.

No more `try_new` or `try_new_async` or `try_new_sync` functions, instead use `SupergraphFetcherBuilder`, `PersistedDocumentsManagerBuilder` and `UsageAgentBuilder` structs to create instances.

Benefits;

- No need to provide all parameters at once when creating an instance even for default values.

Example;
```rust
// Before
let fetcher = SupergraphFetcher::try_new_async(
"SOME_ENDPOINT", // endpoint
"SOME_KEY",
"MyUserAgent/1.0".to_string(),
Duration::from_secs(5), // connect_timeout
Duration::from_secs(10), // request_timeout
false, // accept_invalid_certs
3, // retry_count
)?;

// After
// No need to provide all parameters at once, can use default values
let fetcher = SupergraphFetcherBuilder::new()
.endpoint("SOME_ENDPOINT".to_string())
.key("SOME_KEY".to_string())
.build_async()?;
```

- Easier to add new configuration options in the future without breaking existing code.

Example;

```rust
let fetcher = SupergraphFetcher::try_new_async(
"SOME_ENDPOINT", // endpoint
"SOME_KEY",
"MyUserAgent/1.0".to_string(),
Duration::from_secs(5), // connect_timeout
Duration::from_secs(10), // request_timeout
false, // accept_invalid_certs
3, // retry_count
circuit_breaker_config, // Breaking Change -> new parameter added
)?;

let fetcher = SupergraphFetcherBuilder::new()
.endpoint("SOME_ENDPOINT".to_string())
.key("SOME_KEY".to_string())
.build_async()?; // No breaking change, circuit_breaker_config can be added later if needed
```
20 changes: 20 additions & 0 deletions .changeset/light-walls-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'hive-console-sdk-rs': patch
---

Circuit Breaker Implementation and Multiple Endpoints Support

Implementation of Circuit Breakers in Hive Console Rust SDK, you can learn more [here](https://the-guild.dev/graphql/hive/product-updates/2025-12-04-cdn-mirror-and-circuit-breaker)

Breaking Changes:

Now `endpoint` configuration accepts multiple endpoints as an array for `SupergraphFetcherBuilder` and `PersistedDocumentsManager`.

```diff
SupergraphFetcherBuilder::default()
- .endpoint(endpoint)
+ .add_endpoint(endpoint1)
+ .add_endpoint(endpoint2)
```

This change requires updating the configuration structure to accommodate multiple endpoints.
17 changes: 17 additions & 0 deletions .changeset/violet-waves-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'hive-apollo-router-plugin': major
---

- Multiple endpoints support for `HiveRegistry` and `PersistedOperationsPlugin`

Breaking Changes:
- Now there is no `endpoint` field in the configuration, it has been replaced with `endpoints`, which is an array of strings. You are not affected if you use environment variables to set the endpoint.

```diff
HiveRegistry::new(
Some(
HiveRegistryConfig {
- endpoint: String::from("CDN_ENDPOINT"),
+ endpoints: vec![String::from("CDN_ENDPOINT1"), String::from("CDN_ENDPOINT2")],
)
)
1 change: 1 addition & 0 deletions .github/workflows/tests-integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
integration:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
# Divide integration tests into 3 shards, to run them in parallel.
shardIndex: [1, 2, 3, 'apollo-router']
Expand Down
30 changes: 14 additions & 16 deletions configs/cargo/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 21 additions & 14 deletions integration-tests/tests/apollo-router/apollo-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { getServiceHost } from 'testkit/utils';
import { execa } from '@esm2cjs/execa';

describe('Apollo Router Integration', () => {
const getBaseEndpoint = () =>
getServiceHost('server', 8082).then(v => `http://${v}/artifacts/v1/`);
const getAvailablePort = () =>
new Promise<number>(resolve => {
const server = createServer();
Expand All @@ -18,12 +16,12 @@ describe('Apollo Router Integration', () => {
if (address && typeof address === 'object') {
const port = address.port;
server.close(() => resolve(port));
} else {
throw new Error('Could not get available port');
}
});
});
it('fetches the supergraph and sends usage reports', async () => {
const routerConfigPath = join(tmpdir(), `apollo-router-config-${Date.now()}.yaml`);
const endpointBaseUrl = await getBaseEndpoint();
const { createOrg } = await initSeed().createOwner();
const { createProject } = await createOrg();
const { createTargetAccessToken, createCdnAccess, target, waitForOperationsCollected } =
Expand All @@ -50,6 +48,7 @@ describe('Apollo Router Integration', () => {
.then(r => r.expectNoGraphQLErrors());

expect(publishSchemaResult.schemaPublish.__typename).toBe('SchemaPublishSuccess');

const cdnAccessResult = await createCdnAccess();

const usageAddress = await getServiceHost('usage', 8081);
Expand All @@ -60,24 +59,30 @@ describe('Apollo Router Integration', () => {
`Apollo Router binary not found at path: ${routerBinPath}, make sure to build it first with 'cargo build'`,
);
}

const routerPort = await getAvailablePort();
const routerConfigContent = `
supergraph:
listen: 0.0.0.0:${routerPort}
plugins:
hive.usage: {}
`.trim();
const routerConfigPath = join(tmpdir(), `apollo-router-config-${Date.now()}.yaml`);
writeFileSync(routerConfigPath, routerConfigContent, 'utf-8');

const cdnEndpoint = await getServiceHost('server', 8082).then(
v => `http://${v}/artifacts/v1/${target.id}`,
);
const routerProc = execa(routerBinPath, ['--dev', '--config', routerConfigPath], {
all: true,
env: {
HIVE_CDN_ENDPOINT: endpointBaseUrl + target.id,
HIVE_CDN_ENDPOINT: cdnEndpoint,
HIVE_CDN_KEY: cdnAccessResult.secretAccessToken,
HIVE_ENDPOINT: `http://${usageAddress}`,
HIVE_TOKEN: writeToken.secret,
HIVE_TARGET_ID: target.id,
},
});
let log = '';
await new Promise((resolve, reject) => {
routerProc.catch(err => {
if (!err.isCanceled) {
Expand All @@ -88,7 +93,6 @@ plugins:
if (!routerProcOut) {
return reject(new Error('No stdout from Apollo Router process'));
}
let log = '';
routerProcOut.on('data', data => {
log += data.toString();
if (log.includes('GraphQL endpoint exposed at')) {
Expand All @@ -107,13 +111,13 @@ plugins:
},
body: JSON.stringify({
query: `
query TestQuery {
me {
id
name
}
}
`,
query TestQuery {
me {
id
name
}
}
`,
}),
});

Expand All @@ -128,6 +132,9 @@ plugins:
},
});
await waitForOperationsCollected(1);
} catch (e) {
console.error('Router logs:\n', log);
throw e;
} finally {
routerProc.cancel();
rmSync(routerConfigPath);
Expand Down
Loading
Loading