/** * Script for seeding app deployments into an existing target. * * Requirements: * - Docker Compose is started (pnpm start) * - FEATURE_FLAGS_APP_DEPLOYMENTS_ENABLED=1 in server .env * - A published schema in the target * * Example: * `TOKEN= pnpm seed:app-deployments` * * Where is a Target Access Token from the target's Settings page. */ const token = process.env.TOKEN || process.env.HIVE_TOKEN; if (!token) { console.error('Missing "TOKEN" environment variable.'); console.error('Usage: TOKEN= pnpm seed:app-deployments'); process.exit(1); } const graphqlEndpoint = 'http://localhost:3001/graphql'; async function executeGraphQL(query: string, variables: Record): Promise { const response = await fetch(graphqlEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ query, variables }), }); const result = (await response.json()) as { data?: T; errors?: Array<{ message: string }> }; if (result.errors?.length) { throw new Error(`GraphQL Error: ${result.errors.map(e => e.message).join(', ')}`); } return result.data as T; } const CreateAppDeployment = /* GraphQL */ ` mutation CreateAppDeployment($input: CreateAppDeploymentInput!) { createAppDeployment(input: $input) { error { message } ok { createdAppDeployment { id name version status } } } } `; const AddDocumentsToAppDeployment = /* GraphQL */ ` mutation AddDocumentsToAppDeployment($input: AddDocumentsToAppDeploymentInput!) { addDocumentsToAppDeployment(input: $input) { error { message } ok { appDeployment { id name version status } } } } `; const ActivateAppDeployment = /* GraphQL */ ` mutation ActivateAppDeployment($input: ActivateAppDeploymentInput!) { activateAppDeployment(input: $input) { error { message } ok { activatedAppDeployment { id name version status } } } } `; const RetireAppDeployment = /* GraphQL */ ` mutation RetireAppDeployment($input: RetireAppDeploymentInput!) { retireAppDeployment(input: $input) { error { message } ok { retiredAppDeployment { id name version status } } } } `; // Sample GraphQL documents for app deployments const sampleDocuments = [ { hash: 'get-user-query', body: 'query GetUser($id: ID!) { user(id: $id) { id name email } }' }, { hash: 'list-users-query', body: 'query ListUsers($limit: Int) { users(limit: $limit) { id name } }', }, { hash: 'create-user-mutation', body: 'mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }', }, { hash: 'update-user-mutation', body: 'mutation UpdateUser($id: ID!, $input: UpdateUserInput!) { updateUser(id: $id, input: $input) { id name } }', }, { hash: 'delete-user-mutation', body: 'mutation DeleteUser($id: ID!) { deleteUser(id: $id) }' }, { hash: 'get-products-query', body: 'query GetProducts($category: String) { products(category: $category) { id name price } }', }, { hash: 'get-product-query', body: 'query GetProduct($id: ID!) { product(id: $id) { id name price description } }', }, { hash: 'search-query', body: 'query Search($term: String!) { search(term: $term) { __typename ... on User { id name } ... on Product { id name } } }', }, ]; // App deployment configurations to create const appDeployments = [ { name: 'web-app', versions: ['1.0.0', '1.1.0', '1.2.0', '2.0.0'] }, { name: 'mobile-app', versions: ['3.0.0', '3.1.0', '3.2.0'] }, { name: 'admin-dashboard', versions: ['1.0.0', '1.0.1'] }, { name: 'cli-tool', versions: ['0.1.0', '0.2.0', '1.0.0'] }, ]; async function createAppDeploymentWithDocuments( appName: string, appVersion: string, documents: Array<{ hash: string; body: string }>, shouldActivate: boolean, shouldRetire: boolean = false, ) { // Create the app deployment const createResult = await executeGraphQL<{ createAppDeployment: { error: { message: string } | null; ok: { createdAppDeployment: { id: string; name: string; version: string; status: string }; } | null; }; }>(CreateAppDeployment, { input: { appName, appVersion } }); if (createResult.createAppDeployment.error) { console.error( ` Failed to create ${appName}@${appVersion}:`, createResult.createAppDeployment.error.message, ); return null; } console.log(` Created ${appName}@${appVersion} (pending)`); // Add documents const addDocsResult = await executeGraphQL<{ addDocumentsToAppDeployment: { error: { message: string } | null; ok: { appDeployment: { id: string } } | null; }; }>(AddDocumentsToAppDeployment, { input: { appName, appVersion, documents } }); if (addDocsResult.addDocumentsToAppDeployment.error) { console.error( ` Failed to add documents to ${appName}@${appVersion}:`, addDocsResult.addDocumentsToAppDeployment.error.message, ); return null; } console.log(` Added ${documents.length} documents to ${appName}@${appVersion}`); if (shouldActivate) { const activateResult = await executeGraphQL<{ activateAppDeployment: { error: { message: string } | null; ok: { activatedAppDeployment: { id: string } } | null; }; }>(ActivateAppDeployment, { input: { appName, appVersion } }); if (activateResult.activateAppDeployment.error) { console.error( ` Failed to activate ${appName}@${appVersion}:`, activateResult.activateAppDeployment.error.message, ); return null; } console.log(` Activated ${appName}@${appVersion}`); if (shouldRetire) { const retireResult = await executeGraphQL<{ retireAppDeployment: { error: { message: string } | null; ok: { retiredAppDeployment: { id: string } } | null; }; }>(RetireAppDeployment, { input: { appName, appVersion } }); if (retireResult.retireAppDeployment.error) { console.error( ` Failed to retire ${appName}@${appVersion}:`, retireResult.retireAppDeployment.error.message, ); return null; } console.log(` Retired ${appName}@${appVersion}`); } } return createResult.createAppDeployment.ok?.createdAppDeployment; } console.log(` GraphQL endpoint: ${graphqlEndpoint} `); console.log('Creating app deployments...\n'); for (const app of appDeployments) { console.log(`Creating deployments for ${app.name}:`); for (let i = 0; i < app.versions.length; i++) { const version = app.versions[i]; const isLatest = i === app.versions.length - 1; const isOldest = i === 0; // Select a subset of documents for variety const docsToUse = sampleDocuments.slice(0, 3 + (i % 5)); // Activate all versions, retire old ones (except the latest) const shouldActivate = true; const shouldRetire = !isLatest && isOldest; await createAppDeploymentWithDocuments( app.name, version, docsToUse, shouldActivate, shouldRetire, ); } console.log(''); } // Create one pending deployment (not activated) console.log('Creating a pending deployment:'); await createAppDeploymentWithDocuments( 'beta-app', '0.0.1-beta', sampleDocuments.slice(0, 2), false, // Don't activate ); console.log('\n========================================'); console.log('App deployments seeded successfully!'); console.log('========================================\n');