Skip to content

Commit d8f6e25

Browse files
authored
fix: treat personal and project access tokens as new access tokens (#7248)
1 parent 437277e commit d8f6e25

File tree

6 files changed

+121
-7
lines changed

6 files changed

+121
-7
lines changed

.changeset/empty-rice-follow.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@graphql-hive/core': patch
3+
'@graphql-hive/apollo': patch
4+
'@graphql-hive/envelop': patch
5+
'@graphql-hive/yoga': patch
6+
'hive-console-sdk-rs': patch
7+
---
8+
9+
Support project and personal access tokens (`hvp1/` and `hvu1/`).

packages/libraries/core/src/client/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { createPersistedDocuments } from './persisted-documents.js';
1010
import { createReporting } from './reporting.js';
1111
import type { HiveClient, HivePluginOptions } from './types.js';
1212
import { createUsage } from './usage.js';
13-
import { createHiveLogger, logIf } from './utils.js';
13+
import { createHiveLogger, isLegacyAccessToken, logIf } from './utils.js';
1414

1515
export function createHive(options: HivePluginOptions): HiveClient {
1616
const logger = createHiveLogger(options?.agent?.logger ?? console, '[hive]');
@@ -53,7 +53,7 @@ export function createHive(options: HivePluginOptions): HiveClient {
5353
await Promise.all([schemaReporter.dispose(), usage.dispose()]);
5454
}
5555

56-
const isOrganizationAccessToken = options.token?.startsWith('hvo1/') === true;
56+
const isOrganizationAccessToken = !isLegacyAccessToken(options.token ?? '');
5757

5858
// enabledOnly when `printTokenInfo` is `true` or `debug` is true and `printTokenInfo` is not `false`
5959
const printTokenInfo =

packages/libraries/core/src/client/usage.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
cache,
2525
cacheDocumentKey,
2626
createHiveLogger,
27+
isLegacyAccessToken,
2728
logIf,
2829
measureDuration,
2930
memo,
@@ -78,15 +79,15 @@ export function createUsage(pluginOptions: HivePluginOptions): UsageCollector {
7879
const excludeSet = new Set(options.exclude ?? []);
7980

8081
/** Access tokens using the `hvo1/` require a target. */
81-
if (!options.target && pluginOptions.token.startsWith('hvo1/')) {
82+
if (!options.target && !isLegacyAccessToken(pluginOptions.token)) {
8283
logger.error(
83-
"Using an organization access token (starting with 'hvo1/') requires providing the 'target' option." +
84+
"Your access token requires providing the 'target' option." +
8485
'\nUsage reporting is disabled.',
8586
);
8687
return noopUsageCollector;
8788
}
8889

89-
if (options.target && !pluginOptions.token.startsWith('hvo1/')) {
90+
if (options.target && isLegacyAccessToken(pluginOptions.token)) {
9091
logger.error(
9192
"Using the 'target' option requires using an organization access token (starting with 'hvo1/')." +
9293
'\nUsage reporting is disabled.',

packages/libraries/core/src/client/utils.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,15 @@ export function createHiveLogger(baseLogger: Logger, prefix: string): HiveLogger
214214
},
215215
};
216216
}
217+
218+
export function isLegacyAccessToken(accessToken: string): boolean {
219+
if (
220+
!accessToken.startsWith('hvo1/') &&
221+
!accessToken.startsWith('hvp1/') &&
222+
!accessToken.startsWith('hvu1/')
223+
) {
224+
return true;
225+
}
226+
227+
return false;
228+
}

packages/libraries/core/tests/usage.spec.ts

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,7 @@ test('retry on non-200', async () => {
748748
`);
749749
});
750750

751-
test('constructs URL with usage.target', async ({ expect }) => {
751+
test('constructs URL with usage.target (hvo1/)', async ({ expect }) => {
752752
const logger = createHiveTestingLogger();
753753
const token = 'hvo1/brrrrt';
754754
const dUrl = Promise.withResolvers<string>();
@@ -793,3 +793,95 @@ test('constructs URL with usage.target', async ({ expect }) => {
793793
expect(url).toEqual('http://localhost/the-guild/graphql-hive/staging');
794794
await hive.dispose();
795795
});
796+
797+
test('constructs URL with usage.target (hvp1/)', async ({ expect }) => {
798+
const logger = createHiveTestingLogger();
799+
const token = 'hvp1/brrrrt';
800+
const dUrl = Promise.withResolvers<string>();
801+
802+
const hive = createHive({
803+
enabled: true,
804+
debug: true,
805+
agent: {
806+
timeout: 500,
807+
maxRetries: 0,
808+
sendInterval: 1,
809+
maxSize: 1,
810+
async fetch(url) {
811+
dUrl.resolve(url.toString());
812+
return new Response('', {
813+
status: 200,
814+
});
815+
},
816+
logger,
817+
},
818+
token,
819+
selfHosting: {
820+
graphqlEndpoint: 'http://localhost:2/graphql',
821+
applicationUrl: 'http://localhost:1',
822+
usageEndpoint: 'http://localhost',
823+
},
824+
usage: {
825+
target: 'the-guild/graphql-hive/staging',
826+
},
827+
});
828+
829+
await hive.collectUsage()(
830+
{
831+
schema,
832+
document: op,
833+
operationName: 'asd',
834+
},
835+
{},
836+
);
837+
838+
const url = await dUrl.promise;
839+
expect(url).toEqual('http://localhost/the-guild/graphql-hive/staging');
840+
await hive.dispose();
841+
});
842+
843+
test('constructs URL with usage.target (hvu1/)', async ({ expect }) => {
844+
const logger = createHiveTestingLogger();
845+
const token = 'hvu1/brrrrt';
846+
const dUrl = Promise.withResolvers<string>();
847+
848+
const hive = createHive({
849+
enabled: true,
850+
debug: true,
851+
agent: {
852+
timeout: 500,
853+
maxRetries: 0,
854+
sendInterval: 1,
855+
maxSize: 1,
856+
async fetch(url) {
857+
dUrl.resolve(url.toString());
858+
return new Response('', {
859+
status: 200,
860+
});
861+
},
862+
logger,
863+
},
864+
token,
865+
selfHosting: {
866+
graphqlEndpoint: 'http://localhost:2/graphql',
867+
applicationUrl: 'http://localhost:1',
868+
usageEndpoint: 'http://localhost',
869+
},
870+
usage: {
871+
target: 'the-guild/graphql-hive/staging',
872+
},
873+
});
874+
875+
await hive.collectUsage()(
876+
{
877+
schema,
878+
document: op,
879+
operationName: 'asd',
880+
},
881+
{},
882+
);
883+
884+
const url = await dUrl.promise;
885+
expect(url).toEqual('http://localhost/the-guild/graphql-hive/staging');
886+
await hive.dispose();
887+
});

packages/libraries/sdk-rs/src/agent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ impl UsageAgent {
165165

166166
let mut endpoint = endpoint;
167167

168-
if token.starts_with("hvo1/") {
168+
if token.starts_with("hvo1/") || token.starts_with("hvu1/") || token.starts_with("hvp1/") {
169169
if let Some(target_id) = target_id {
170170
endpoint.push_str(&format!("/{}", target_id));
171171
}

0 commit comments

Comments
 (0)