mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
1825 lines
45 KiB
TypeScript
1825 lines
45 KiB
TypeScript
import { buildASTSchema, parse } from 'graphql';
|
|
import { createLogger } from 'graphql-yoga';
|
|
import { waitFor } from 'testkit/flow';
|
|
import { initSeed } from 'testkit/seed';
|
|
import { getServiceHost } from 'testkit/utils';
|
|
import { createHive } from '@graphql-hive/core';
|
|
import { graphql } from '../../testkit/gql';
|
|
import { execute } from '../../testkit/graphql';
|
|
|
|
const CreateAppDeployment = graphql(`
|
|
mutation CreateAppDeployment($input: CreateAppDeploymentInput!) {
|
|
createAppDeployment(input: $input) {
|
|
error {
|
|
message
|
|
details {
|
|
appName
|
|
appVersion
|
|
}
|
|
}
|
|
ok {
|
|
createdAppDeployment {
|
|
id
|
|
name
|
|
version
|
|
status
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const GetAppDeployment = graphql(`
|
|
query GetAppDeployment(
|
|
$targetSelector: TargetSelectorInput!
|
|
$appDeploymentName: String!
|
|
$appDeploymentVersion: String!
|
|
) {
|
|
target(selector: $targetSelector) {
|
|
appDeployment(appName: $appDeploymentName, appVersion: $appDeploymentVersion) {
|
|
id
|
|
lastUsed
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const AddDocumentsToAppDeployment = graphql(`
|
|
mutation AddDocumentsToAppDeployment($input: AddDocumentsToAppDeploymentInput!) {
|
|
addDocumentsToAppDeployment(input: $input) {
|
|
error {
|
|
message
|
|
details {
|
|
index
|
|
message
|
|
}
|
|
}
|
|
ok {
|
|
appDeployment {
|
|
id
|
|
name
|
|
version
|
|
status
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const ActivateAppDeployment = graphql(`
|
|
mutation ActivateAppDeployment($input: ActivateAppDeploymentInput!) {
|
|
activateAppDeployment(input: $input) {
|
|
error {
|
|
message
|
|
}
|
|
ok {
|
|
isSkipped
|
|
activatedAppDeployment {
|
|
id
|
|
name
|
|
version
|
|
status
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const RetireAppDeployment = graphql(`
|
|
mutation RetireAppDeployment($input: RetireAppDeploymentInput!) {
|
|
retireAppDeployment(input: $input) {
|
|
error {
|
|
message
|
|
}
|
|
|
|
ok {
|
|
retiredAppDeployment {
|
|
id
|
|
name
|
|
version
|
|
status
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
const GetPaginatedPersistedDocuments = graphql(`
|
|
query GetPaginatedPersistedDocuments(
|
|
$targetSelector: TargetSelectorInput!
|
|
$appDeploymentName: String!
|
|
$appDeploymentVersion: String!
|
|
$first: Int
|
|
$cursor: String
|
|
) {
|
|
target(selector: $targetSelector) {
|
|
appDeployment(appName: $appDeploymentName, appVersion: $appDeploymentVersion) {
|
|
id
|
|
documents(first: $first, after: $cursor) {
|
|
edges {
|
|
cursor
|
|
node {
|
|
hash
|
|
body
|
|
}
|
|
}
|
|
pageInfo {
|
|
hasNextPage
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`);
|
|
|
|
test('create app deployment, add operations, publish, access via CDN (happy path)', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, createCdnAccess } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const cdnAccess = await createCdnAccess();
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
appDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { activateAppDeployment } = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(activateAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
activatedAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'active',
|
|
},
|
|
isSkipped: false,
|
|
},
|
|
});
|
|
|
|
const persistedOperationUrl = `${cdnAccess.cdnUrl}/apps/my-app/1.0.0/hash`;
|
|
const response = await fetch(persistedOperationUrl, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Hive-CDN-Key': cdnAccess.secretAccessToken,
|
|
},
|
|
});
|
|
const txt = await response.text();
|
|
expect(txt).toEqual('query { hello }');
|
|
expect(response.status).toBe(200);
|
|
});
|
|
|
|
test('create app deployment with same name and version succeed if deployment is not active', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
let createAppDeployment = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
})
|
|
.then(res => res.expectNoGraphQLErrors())
|
|
.then(res => res.createAppDeployment);
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
createAppDeployment = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
})
|
|
.then(res => res.expectNoGraphQLErrors())
|
|
.then(res => res.createAppDeployment);
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
test('create app deployment with same name and version does not fail if deployment is active', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
let createAppDeployment = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
})
|
|
.then(res => res.expectNoGraphQLErrors())
|
|
.then(res => res.createAppDeployment);
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { activateAppDeployment } = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(activateAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
activatedAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'active',
|
|
},
|
|
isSkipped: false,
|
|
},
|
|
});
|
|
|
|
createAppDeployment = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
})
|
|
.then(res => res.expectNoGraphQLErrors())
|
|
.then(res => res.createAppDeployment);
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'active',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
test('create app deployment fails if app name is empty', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: '',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
appName: 'Must be at least 1 character long',
|
|
appVersion: null,
|
|
},
|
|
message: 'Invalid input',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('create app deployment fails if app name exceeds length of 256 characters', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: new Array(257).fill('a').join(''),
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
appName: 'Must be at most 64 characters long',
|
|
appVersion: null,
|
|
},
|
|
message: 'Invalid input',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('create app deployment fails if app version is empty', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'myapp',
|
|
appVersion: '',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
appName: null,
|
|
appVersion: 'Must be at least 1 character long',
|
|
},
|
|
message: 'Invalid input',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('create app deployment fails if app version exceeds length of 256 characters', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: new Array(257).fill('a').join(''),
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
appName: null,
|
|
appVersion: 'Must be at most 64 characters long',
|
|
},
|
|
message: 'Invalid input',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('create app deployment fails without feature flag enabled for organization', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject } = await createOrg();
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: {
|
|
details: null,
|
|
message:
|
|
'This organization has no access to app deployments. Please contact the Hive team for early access.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if there is no initial schema published', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: null,
|
|
message: 'No schema has been published yet',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if document hash is less than 1 character', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: '',
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
index: 0,
|
|
message: 'Hash must be at least 1 characters long',
|
|
},
|
|
message: 'Invalid input, please check the operations.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if document hash is longer than 256 characters', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: new Array(129).fill('a').join(''),
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
index: 0,
|
|
message: 'Hash must be at most 128 characters long',
|
|
},
|
|
message: 'Invalid input, please check the operations.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if document is not parse-able', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'qugu',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
index: 0,
|
|
message: 'Syntax Error: Unexpected Name "qugu".',
|
|
},
|
|
message: 'Failed to parse a GraphQL operation.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if document does not pass validation against the target schema', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query { hi }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
index: 0,
|
|
message: 'Cannot query field "hi" on type "Query".',
|
|
},
|
|
message: 'The GraphQL operation is not valid against the latest schema version.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if document contains multiple executable operation definitions', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(createAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
createdAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
version: '1.0.0',
|
|
status: 'pending',
|
|
},
|
|
},
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query a { hello } query b { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: {
|
|
index: 0,
|
|
message:
|
|
'Multiple operation definitions found. Only one executable operation definition is allowed per document.',
|
|
},
|
|
message: 'Only one executable operation definition is allowed per document.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails if app deployment does not exist', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: null,
|
|
message: 'App deployment not found',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('add documents to app deployment fails without feature flag enabled for organization', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject } = await createOrg();
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(addDocumentsToAppDeployment).toEqual({
|
|
error: {
|
|
details: null,
|
|
message:
|
|
'This organization has no access to app deployments. Please contact the Hive team for early access.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('activate app deployment fails if app deployment does not exist', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { activateAppDeployment } = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(activateAppDeployment).toEqual({
|
|
error: {
|
|
message: 'App deployment not found',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('activate app deployment succeeds if app deployment is already active', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
let activateResult = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(activateResult).toEqual({
|
|
activateAppDeployment: {
|
|
error: null,
|
|
ok: {
|
|
activatedAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
status: 'active',
|
|
version: '1.0.0',
|
|
},
|
|
isSkipped: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
activateResult = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(activateResult).toEqual({
|
|
activateAppDeployment: {
|
|
error: null,
|
|
ok: {
|
|
activatedAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
status: 'active',
|
|
version: '1.0.0',
|
|
},
|
|
isSkipped: true,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
test('activate app deployment fails if app deployment is retired', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
let activateResult = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(activateResult).toEqual({
|
|
activateAppDeployment: {
|
|
error: null,
|
|
ok: {
|
|
activatedAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
status: 'active',
|
|
version: '1.0.0',
|
|
},
|
|
isSkipped: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
const retireResult = await execute({
|
|
document: RetireAppDeployment,
|
|
variables: {
|
|
input: {
|
|
targetId: target.id,
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(retireResult).toEqual({
|
|
retireAppDeployment: {
|
|
error: null,
|
|
ok: {
|
|
retiredAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
status: 'active',
|
|
version: '1.0.0',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
activateResult = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(activateResult).toEqual({
|
|
activateAppDeployment: {
|
|
error: {
|
|
message: 'App deployment is retired',
|
|
},
|
|
ok: null,
|
|
},
|
|
});
|
|
});
|
|
|
|
test('retire app deployment fails if app deployment does not exist', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { retireAppDeployment } = await execute({
|
|
document: RetireAppDeployment,
|
|
variables: {
|
|
input: {
|
|
targetId: target.id,
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(retireAppDeployment).toEqual({
|
|
error: {
|
|
message: 'App deployment not found',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('retire app deployment fails if app deployment is pending (not active)', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
const { retireAppDeployment } = await execute({
|
|
document: RetireAppDeployment,
|
|
variables: {
|
|
input: {
|
|
targetId: target.id,
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(retireAppDeployment).toEqual({
|
|
error: {
|
|
message: 'App deployment is not active',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('retire app deployment succeeds if app deployment is active', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
const { retireAppDeployment } = await execute({
|
|
document: RetireAppDeployment,
|
|
variables: {
|
|
input: {
|
|
targetId: target.id,
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(retireAppDeployment).toEqual({
|
|
error: null,
|
|
ok: {
|
|
retiredAppDeployment: {
|
|
id: expect.any(String),
|
|
name: 'my-app',
|
|
status: 'active',
|
|
version: '1.0.0',
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
test('retire app deployments makes the persisted operations unavailable via CDN', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, createCdnAccess, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
hello: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const cdnAccess = await createCdnAccess();
|
|
|
|
await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
documents: [
|
|
{
|
|
hash: 'hash',
|
|
body: 'query { hello }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
const persistedOperationUrl = `${cdnAccess.cdnUrl}/apps/my-app/1.0.0/hash`;
|
|
let response = await fetch(persistedOperationUrl, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Hive-CDN-Key': cdnAccess.secretAccessToken,
|
|
},
|
|
});
|
|
|
|
expect(response.status).toBe(200);
|
|
|
|
await execute({
|
|
document: RetireAppDeployment,
|
|
variables: {
|
|
input: {
|
|
targetId: target.id,
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
response = await fetch(persistedOperationUrl, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-Hive-CDN-Key': cdnAccess.secretAccessToken,
|
|
},
|
|
});
|
|
|
|
expect(response.status).toBe(404);
|
|
});
|
|
|
|
test('retire app deployments fails without feature flag enabled for organization', async () => {
|
|
const { createOrg } = await initSeed().createOwner();
|
|
const { createProject } = await createOrg();
|
|
const { createTargetAccessToken, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { retireAppDeployment } = await execute({
|
|
document: RetireAppDeployment,
|
|
variables: {
|
|
input: {
|
|
targetId: target.id,
|
|
appName: 'my-app',
|
|
appVersion: '1.0.0',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
|
|
expect(retireAppDeployment).toEqual({
|
|
error: {
|
|
message:
|
|
'This organization has no access to app deployments. Please contact the Hive team for early access.',
|
|
},
|
|
ok: null,
|
|
});
|
|
});
|
|
|
|
test('get app deployment documents via GraphQL API', async () => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag, organization } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, project, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(createAppDeployment.error).toBeNull();
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
a: String
|
|
b: String
|
|
c: String
|
|
d: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
documents: [
|
|
{
|
|
hash: 'aaa',
|
|
body: 'query { a }',
|
|
},
|
|
{
|
|
hash: 'bbb',
|
|
body: 'query { b }',
|
|
},
|
|
{
|
|
hash: 'ccc',
|
|
body: 'query { c }',
|
|
},
|
|
{
|
|
hash: 'ddd',
|
|
body: 'query { d }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(addDocumentsToAppDeployment.error).toBeNull();
|
|
|
|
const result = await execute({
|
|
document: GetPaginatedPersistedDocuments,
|
|
variables: {
|
|
targetSelector: {
|
|
organizationSlug: organization.slug,
|
|
projectSlug: project.slug,
|
|
targetSlug: target.slug,
|
|
},
|
|
appDeploymentName: 'app-name',
|
|
appDeploymentVersion: 'app-version',
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(result.target).toMatchObject({
|
|
appDeployment: {
|
|
documents: {
|
|
edges: [
|
|
{
|
|
cursor: 'YWFh',
|
|
node: {
|
|
body: 'query { a }',
|
|
hash: 'aaa',
|
|
},
|
|
},
|
|
{
|
|
cursor: 'YmJi',
|
|
node: {
|
|
body: 'query { b }',
|
|
hash: 'bbb',
|
|
},
|
|
},
|
|
{
|
|
cursor: 'Y2Nj',
|
|
node: {
|
|
body: 'query { c }',
|
|
hash: 'ccc',
|
|
},
|
|
},
|
|
{
|
|
cursor: 'ZGRk',
|
|
node: {
|
|
body: 'query { d }',
|
|
hash: 'ddd',
|
|
},
|
|
},
|
|
],
|
|
pageInfo: {
|
|
hasNextPage: false,
|
|
},
|
|
},
|
|
id: expect.any(String),
|
|
},
|
|
});
|
|
});
|
|
|
|
test('paginate app deployment documents via GraphQL API', async () => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag, organization } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, project, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(createAppDeployment.error).toBeNull();
|
|
|
|
await token.publishSchema({
|
|
sdl: /* GraphQL */ `
|
|
type Query {
|
|
a: String
|
|
b: String
|
|
c: String
|
|
d: String
|
|
}
|
|
`,
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
documents: [
|
|
{
|
|
hash: 'aaa',
|
|
body: 'query { a }',
|
|
},
|
|
{
|
|
hash: 'bbb',
|
|
body: 'query { b }',
|
|
},
|
|
{
|
|
hash: 'ccc',
|
|
body: 'query { c }',
|
|
},
|
|
{
|
|
hash: 'ddd',
|
|
body: 'query { d }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(addDocumentsToAppDeployment.error).toBeNull();
|
|
|
|
let result = await execute({
|
|
document: GetPaginatedPersistedDocuments,
|
|
variables: {
|
|
targetSelector: {
|
|
organizationSlug: organization.slug,
|
|
projectSlug: project.slug,
|
|
targetSlug: target.slug,
|
|
},
|
|
appDeploymentName: 'app-name',
|
|
appDeploymentVersion: 'app-version',
|
|
first: 1,
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(result.target).toMatchObject({
|
|
appDeployment: {
|
|
documents: {
|
|
edges: [
|
|
{
|
|
cursor: 'YWFh',
|
|
node: {
|
|
body: 'query { a }',
|
|
hash: 'aaa',
|
|
},
|
|
},
|
|
],
|
|
pageInfo: {
|
|
hasNextPage: true,
|
|
},
|
|
},
|
|
id: expect.any(String),
|
|
},
|
|
});
|
|
result = await execute({
|
|
document: GetPaginatedPersistedDocuments,
|
|
variables: {
|
|
targetSelector: {
|
|
organizationSlug: organization.slug,
|
|
projectSlug: project.slug,
|
|
targetSlug: target.slug,
|
|
},
|
|
appDeploymentName: 'app-name',
|
|
appDeploymentVersion: 'app-version',
|
|
first: 1,
|
|
cursor: 'YWFh',
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(result.target).toMatchObject({
|
|
appDeployment: {
|
|
documents: {
|
|
edges: [
|
|
{
|
|
cursor: 'YmJi',
|
|
node: {
|
|
body: 'query { b }',
|
|
hash: 'bbb',
|
|
},
|
|
},
|
|
],
|
|
pageInfo: {
|
|
hasNextPage: true,
|
|
},
|
|
},
|
|
id: expect.any(String),
|
|
},
|
|
});
|
|
result = await execute({
|
|
document: GetPaginatedPersistedDocuments,
|
|
variables: {
|
|
targetSelector: {
|
|
organizationSlug: organization.slug,
|
|
projectSlug: project.slug,
|
|
targetSlug: target.slug,
|
|
},
|
|
appDeploymentName: 'app-name',
|
|
appDeploymentVersion: 'app-version',
|
|
cursor: 'YmJi',
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(result.target).toMatchObject({
|
|
appDeployment: {
|
|
documents: {
|
|
edges: [
|
|
{
|
|
cursor: 'Y2Nj',
|
|
node: {
|
|
body: 'query { c }',
|
|
hash: 'ccc',
|
|
},
|
|
},
|
|
{
|
|
cursor: 'ZGRk',
|
|
node: {
|
|
body: 'query { d }',
|
|
hash: 'ddd',
|
|
},
|
|
},
|
|
],
|
|
pageInfo: {
|
|
hasNextPage: false,
|
|
},
|
|
},
|
|
id: expect.any(String),
|
|
},
|
|
});
|
|
});
|
|
|
|
test('app deployment usage reporting', async () => {
|
|
const { createOrg, ownerToken } = await initSeed().createOwner();
|
|
const { createProject, setFeatureFlag, organization } = await createOrg();
|
|
await setFeatureFlag('appDeployments', true);
|
|
const { createTargetAccessToken, project, target } = await createProject();
|
|
const token = await createTargetAccessToken({});
|
|
|
|
const { createAppDeployment } = await execute({
|
|
document: CreateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(createAppDeployment.error).toBeNull();
|
|
|
|
const sdl = /* GraphQL */ `
|
|
type Query {
|
|
a: String
|
|
b: String
|
|
c: String
|
|
d: String
|
|
}
|
|
`;
|
|
|
|
await token.publishSchema({
|
|
sdl,
|
|
});
|
|
|
|
const { addDocumentsToAppDeployment } = await execute({
|
|
document: AddDocumentsToAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
documents: [
|
|
{
|
|
hash: 'aaa',
|
|
body: 'query { a }',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(addDocumentsToAppDeployment.error).toBeNull();
|
|
|
|
const { activateAppDeployment } = await execute({
|
|
document: ActivateAppDeployment,
|
|
variables: {
|
|
input: {
|
|
appName: 'app-name',
|
|
appVersion: 'app-version',
|
|
},
|
|
},
|
|
authToken: token.secret,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(activateAppDeployment.error).toEqual(null);
|
|
|
|
let data = await execute({
|
|
document: GetAppDeployment,
|
|
variables: {
|
|
targetSelector: {
|
|
organizationSlug: organization.slug,
|
|
projectSlug: project.slug,
|
|
targetSlug: target.slug,
|
|
},
|
|
appDeploymentName: 'app-name',
|
|
appDeploymentVersion: 'app-version',
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(data.target?.appDeployment?.lastUsed).toEqual(null);
|
|
|
|
const usageAddress = await getServiceHost('usage', 8081);
|
|
|
|
const client = createHive({
|
|
enabled: true,
|
|
token: token.secret,
|
|
usage: true,
|
|
debug: false,
|
|
agent: {
|
|
logger: createLogger('debug'),
|
|
maxSize: 1,
|
|
},
|
|
selfHosting: {
|
|
usageEndpoint: 'http://' + usageAddress,
|
|
graphqlEndpoint: 'http://noop/',
|
|
applicationUrl: 'http://noop/',
|
|
},
|
|
});
|
|
|
|
const request = new Request('http://localhost:4000/graphql', {
|
|
method: 'POST',
|
|
headers: {
|
|
'x-graphql-client-name': 'app-name',
|
|
'x-graphql-client-version': 'app-version',
|
|
},
|
|
});
|
|
|
|
await client.collectUsage()(
|
|
{
|
|
document: parse(`query { a }`),
|
|
schema: buildASTSchema(parse(sdl)),
|
|
contextValue: { request },
|
|
},
|
|
{},
|
|
'app-name~app-version~aaa',
|
|
);
|
|
|
|
await waitFor(5000);
|
|
|
|
data = await execute({
|
|
document: GetAppDeployment,
|
|
variables: {
|
|
targetSelector: {
|
|
organizationSlug: organization.slug,
|
|
projectSlug: project.slug,
|
|
targetSlug: target.slug,
|
|
},
|
|
appDeploymentName: 'app-name',
|
|
appDeploymentVersion: 'app-version',
|
|
},
|
|
authToken: ownerToken,
|
|
}).then(res => res.expectNoGraphQLErrors());
|
|
expect(data.target?.appDeployment?.lastUsed).toEqual(expect.any(String));
|
|
});
|