Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
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
5 changes: 5 additions & 0 deletions .changeset/five-hoops-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-hive/cli': minor
---

Improve output of the `hive whoami` command. It now also handles the new access token format.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a change for hive also for the token scoping.

11 changes: 10 additions & 1 deletion integration-tests/testkit/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import {
import { execute } from './graphql';
import { UpdateSchemaPolicyForOrganization, UpdateSchemaPolicyForProject } from './schema-policy';
import { collect, CollectedOperation, legacyCollect } from './usage';
import { generateUnique } from './utils';
import { generateUnique, getServiceHost } from './utils';

export function initSeed() {
function createConnectionPool() {
Expand Down Expand Up @@ -89,6 +89,15 @@ export function initSeed() {
},
authenticate,
generateEmail: () => userEmail(generateUnique()),
async purgeOrganizationAccessTokenById(id: string) {
const registryAddress = await getServiceHost('server', 8082);
await fetch(
'http://' + registryAddress + '/cache/organization-access-token-cache/delete/' + id,
{
method: 'POST',
},
).then(res => res.json());
},
async createOwner() {
const ownerEmail = userEmail(generateUnique());
const auth = await authenticate(ownerEmail);
Expand Down
9 changes: 9 additions & 0 deletions integration-tests/testkit/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,12 @@ export function assertNonNull<T>(
throw new Error(message);
}
}

export function assertNonNullish<T>(
value: T | null | undefined,
message = 'Expected non-null value.',
): asserts value is T {
if (value === null) {
throw new Error(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,8 @@
import { graphql } from '../../testkit/gql';
import * as GraphQLSchema from '../../testkit/gql/graphql';
import { execute } from '../../testkit/graphql';
import { initSeed } from '../../testkit/seed';

const CreateOrganizationAccessTokenMutation = graphql(`
mutation CreateOrganizationAccessToken($input: CreateOrganizationAccessTokenInput!) {
createOrganizationAccessToken(input: $input) {
ok {
privateAccessKey
createdOrganizationAccessToken {
id
title
description
permissions
createdAt
}
}
error {
message
details {
title
description
}
}
}
}
`);
import { createOrganizationAccessToken } from 'testkit/flow';
import { graphql } from '../../../testkit/gql';
import * as GraphQLSchema from '../../../testkit/gql/graphql';
import { execute } from '../../../testkit/graphql';
import { initSeed } from '../../../testkit/seed';

const OrganizationProjectTargetQuery = graphql(`
query OrganizationProjectTargetQuery(
Expand Down Expand Up @@ -60,7 +37,6 @@ const PaginatedAccessTokensQuery = graphql(`
id
title
description
permissions
createdAt
}
}
Expand All @@ -77,21 +53,19 @@ test.concurrent('create: success', async () => {
const { createOrg, ownerToken } = await initSeed().createOwner();
const org = await createOrg();

const result = await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
const result = await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());

expect(result.createOrganizationAccessToken.error).toEqual(null);
expect(result.createOrganizationAccessToken.ok).toEqual({
privateAccessKey: expect.any(String),
Expand All @@ -109,21 +83,18 @@ test.concurrent('create: failure invalid title', async ({ expect }) => {
const { createOrg, ownerToken } = await initSeed().createOwner();
const org = await createOrg();

const result = await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: ' ',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
const result = await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: ' ',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());
expect(result.createOrganizationAccessToken.ok).toEqual(null);
expect(result.createOrganizationAccessToken.error).toMatchInlineSnapshot(`
{
Expand All @@ -140,21 +111,18 @@ test.concurrent('create: failure invalid description', async ({ expect }) => {
const { createOrg, ownerToken } = await initSeed().createOwner();
const org = await createOrg();

const result = await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: new Array(300).fill('A').join(''),
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
const result = await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: new Array(300).fill('A').join(''),
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());
expect(result.createOrganizationAccessToken.ok).toEqual(null);
expect(result.createOrganizationAccessToken.error).toMatchInlineSnapshot(`
{
Expand All @@ -172,21 +140,18 @@ test.concurrent('create: failure because no access to organization', async ({ ex
const actor2 = await initSeed().createOwner();
const org = await actor1.createOrg();

const errors = await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
const errors = await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'Some description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: [],
},
authToken: actor2.ownerToken,
}).then(e => e.expectGraphQLErrors());
actor2.ownerToken,
).then(e => e.expectGraphQLErrors());
expect(errors).toMatchObject([
{
extensions: {
Expand All @@ -204,21 +169,18 @@ test.concurrent('query GraphQL API on resources with access', async ({ expect })
const org = await createOrg();
const project = await org.createProject(GraphQLSchema.ProjectType.Federation);

const result = await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'a description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: ['organization:describe', 'project:describe'],
const result = await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'a description',
resources: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
permissions: ['organization:describe', 'project:describe'],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());
expect(result.createOrganizationAccessToken.error).toEqual(null);
const organizationAccessToken = result.createOrganizationAccessToken.ok!.privateAccessKey;

Expand Down Expand Up @@ -253,29 +215,26 @@ test.concurrent('query GraphQL API on resources without access', async ({ expect
const project1 = await org.createProject(GraphQLSchema.ProjectType.Federation);
const project2 = await org.createProject(GraphQLSchema.ProjectType.Federation);

const result = await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project1.project.id,
targets: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
},
],
},
permissions: ['organization:describe', 'project:describe'],
const result = await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'a access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentModeType.Granular,
projects: [
{
projectId: project1.project.id,
targets: { mode: GraphQLSchema.ResourceAssignmentModeType.All },
},
],
},
permissions: ['organization:describe', 'project:describe'],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());
expect(result.createOrganizationAccessToken.error).toEqual(null);
const organizationAccessToken = result.createOrganizationAccessToken.ok!.privateAccessKey;

Expand Down Expand Up @@ -317,41 +276,35 @@ test.concurrent('pagination', async ({ expect }) => {
},
});

await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'first access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
permissions: ['organization:describe'],
await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'first access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
permissions: ['organization:describe'],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());

await execute({
document: CreateOrganizationAccessTokenMutation,
variables: {
input: {
organization: {
byId: org.organization.id,
},
title: 'second access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
permissions: ['organization:describe'],
await createOrganizationAccessToken(
{
organization: {
byId: org.organization.id,
},
title: 'second access token',
description: 'a description',
resources: {
mode: GraphQLSchema.ResourceAssignmentModeType.All,
},
permissions: ['organization:describe'],
},
authToken: ownerToken,
}).then(e => e.expectNoGraphQLErrors());
ownerToken,
).then(e => e.expectNoGraphQLErrors());

paginatedResult = await execute({
document: PaginatedAccessTokensQuery,
Expand Down
Loading
Loading