From b007286f4ad6716554c99214fdc5004fedb3652e Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Fri, 16 Feb 2024 15:05:52 +0530 Subject: [PATCH 1/8] Add script to publish dummy `ApplicationDeploymentRecord` (#72) * Add script to publish application deployment record * Refactor publish deploy records script * Assert if name doesn't exist in package.json * Disable import environment variables button --------- Co-authored-by: neeraj --- packages/backend/package.json | 1 + packages/backend/src/database.ts | 15 ++-- packages/backend/src/entity/Deployment.ts | 6 +- packages/backend/src/registry.ts | 5 +- .../backend/test/fixtures/deployments.json | 14 ++-- .../backend/test/publish-deploy-records.ts | 77 +++++++++++++++++++ .../id/settings/EnvironmentVariables.tsx | 3 +- 7 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 packages/backend/test/publish-deploy-records.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 75ba8d67..c28f72c7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -37,6 +37,7 @@ "format": "prettier --write .", "format:check": "prettier --check .", "registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts", + "registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts", "db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts", "db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts" }, diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 81c9fbd3..a2959773 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -127,9 +127,7 @@ export class Database { } async getDeploymentsByProjectId (projectId: string): Promise { - const deploymentRepository = this.dataSource.getRepository(Deployment); - - const deployments = await deploymentRepository.find({ + const deployments = await this.getDeployments({ relations: { project: true, domain: true, @@ -148,6 +146,13 @@ export class Database { return deployments; } + async getDeployments (options: FindManyOptions): Promise { + const deploymentRepository = this.dataSource.getRepository(Deployment); + const deployments = await deploymentRepository.find(options); + + return deployments; + } + async getDeployment (options: FindOneOptions): Promise { const deploymentRepository = this.dataSource.getRepository(Deployment); const deployment = await deploymentRepository.findOne(options); @@ -166,12 +171,10 @@ export class Database { const deploymentRepository = this.dataSource.getRepository(Deployment); const id = nanoid(); - const url = `${data.project!.name}-${id}.${PROJECT_DOMAIN}`; const updatedData = { ...data, - id, - url + id }; const deployment = await deploymentRepository.save(updatedData); diff --git a/packages/backend/src/entity/Deployment.ts b/packages/backend/src/entity/Deployment.ts index 2ccb1c0b..76238f32 100644 --- a/packages/backend/src/entity/Deployment.ts +++ b/packages/backend/src/entity/Deployment.ts @@ -28,7 +28,7 @@ export enum DeploymentStatus { export interface ApplicationRecord { type: string; version:string - name?: string + name: string description?: string homepage?: string license?: string @@ -65,8 +65,8 @@ export class Deployment { @Column('varchar') commitMessage!: string; - @Column('varchar') - url!: string; + @Column('varchar', { nullable: true }) + url!: string | null; @Column('varchar') registryRecordId!: string; diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 5c17871a..02dc924b 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -36,6 +36,7 @@ export class Registry { appType: string, repoUrl: string }): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> { + assert(packageJSON.name, "name field doesn't exist in package.json"); // Use laconic-sdk to publish record // Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts/publish-app-record.sh // Fetch previous records @@ -58,7 +59,7 @@ export class Registry { repository_ref: commitHash, repository: [repoUrl], app_type: appType, - ...(packageJSON.name && { name: packageJSON.name }), + name: packageJSON.name, ...(packageJSON.description && { description: packageJSON.description }), ...(packageJSON.homepage && { homepage: packageJSON.homepage }), ...(packageJSON.license && { license: packageJSON.license }), @@ -79,7 +80,7 @@ export class Registry { log('Application record data:', applicationRecord); // TODO: Discuss computation of CRN - const crn = this.getCrn(packageJSON.name ?? ''); + const crn = this.getCrn(packageJSON.name); log(`Setting name: ${crn} for record ID: ${result.data.id}`); await this.registry.setName({ cid: result.data.id, crn }, this.registryConfig.privateKey, this.registryConfig.fee); diff --git a/packages/backend/test/fixtures/deployments.json b/packages/backend/test/fixtures/deployments.json index e0900921..25176713 100644 --- a/packages/backend/test/fixtures/deployments.json +++ b/packages/backend/test/fixtures/deployments.json @@ -4,7 +4,7 @@ "domainIndex":0, "createdByIndex": 0, "id":"ffhae3zq", - "status": "Building", + "status": "Ready", "environment": "Production", "isCurrent": true, "registryRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", @@ -34,7 +34,7 @@ "domainIndex":2, "createdByIndex": 0, "id":"qmgekyte", - "status": "Error", + "status": "Ready", "environment": "Development", "isCurrent": false, "registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", @@ -64,7 +64,7 @@ "domainIndex":3, "createdByIndex": 1, "id":"eO8cckxk", - "status": "Building", + "status": "Ready", "environment": "Production", "isCurrent": true, "registryRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", @@ -94,7 +94,7 @@ "domainIndex":5, "createdByIndex": 1, "id":"hwwr6sbx", - "status": "Error", + "status": "Ready", "environment": "Development", "isCurrent": false, "registryRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", @@ -109,7 +109,7 @@ "domainIndex":9, "createdByIndex": 2, "id":"ndxje48a", - "status": "Building", + "status": "Ready", "environment": "Production", "isCurrent": true, "registryRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", @@ -139,7 +139,7 @@ "domainIndex":8, "createdByIndex": 2, "id":"b4bpthjr", - "status": "Error", + "status": "Ready", "environment": "Development", "isCurrent": false, "registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", @@ -154,7 +154,7 @@ "domainIndex": 6, "createdByIndex": 2, "id":"b4bpthjr", - "status": "Building", + "status": "Ready", "environment": "Production", "isCurrent": true, "registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", diff --git a/packages/backend/test/publish-deploy-records.ts b/packages/backend/test/publish-deploy-records.ts new file mode 100644 index 00000000..6f2d26b0 --- /dev/null +++ b/packages/backend/test/publish-deploy-records.ts @@ -0,0 +1,77 @@ +import debug from 'debug'; +import { DataSource } from 'typeorm'; +import path from 'path'; + +import { Registry } from '@cerc-io/laconic-sdk'; + +import { Config } from '../src/config'; +import { DEFAULT_CONFIG_FILE_PATH, PROJECT_DOMAIN } from '../src/constants'; +import { getConfig } from '../src/utils'; +import { Deployment, DeploymentStatus } from '../src/entity/Deployment'; + +const log = debug('snowball:publish-deploy-records'); + +async function main () { + const { registryConfig, database } = await getConfig(DEFAULT_CONFIG_FILE_PATH); + + const registry = new Registry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId); + + const dataSource = new DataSource({ + type: 'better-sqlite3', + database: database.dbPath, + synchronize: true, + entities: [path.join(__dirname, '../src/entity/*')] + }); + + await dataSource.initialize(); + + const deploymentRepository = dataSource.getRepository(Deployment); + const deployments = await deploymentRepository.find({ + relations: { + project: true + }, + where: { + status: DeploymentStatus.Building + } + }); + + for await (const deployment of deployments) { + const url = `${deployment.project.name}-${deployment.id}.${PROJECT_DOMAIN}`; + + const applicationDeploymentRecord = { + type: 'ApplicationDeploymentRecord', + version: '0.0.1', + name: deployment.registryRecordData.name, + application: deployment.registryRecordId, + + // TODO: Create DNS record + dns: 'bafyreihlymqggsgqiqawvehkpr2imt4l3u6q7um7xzjrux5rhsvwnuyewm', + + // Using dummy values + meta: JSON.stringify({ + config: 'da39a3ee5e6b4b0d3255bfef95601890afd80709', + so: '66fcfa49a1664d4cb4ce4f72c1c0e151' + }), + + request: deployment.project.registryRecordId, + url + }; + + const result = await registry.setRecord( + { + privateKey: registryConfig.privateKey, + record: applicationDeploymentRecord, + bondId: registryConfig.bondId + }, + '', + registryConfig.fee + ); + + log('Application deployment record data:', applicationDeploymentRecord); + log(`Application deployment record published: ${result.data.id}`); + } +} + +main().catch((err) => { + log(err); +}); diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx index 0ae3932b..c0eb59b9 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/EnvironmentVariables.tsx @@ -171,7 +171,8 @@ export const EnvironmentVariablesTabPanel = () => { > + Add variable - From ce55fe62d8d4d55fe276b078c81d965e84579639 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Mon, 19 Feb 2024 13:43:29 +0530 Subject: [PATCH 2/8] Check for `ApplicationDeploymentRecord` and update deployments (#73) * Add skeleton and TODOs for polling deployment records * Add method implementations for fetching deployment records * Handle if deployment url is not set * Add logs after getting ApplicationDeploymentRecord * Add application deployment record to deployment entity * Change type of application deployment record data * Fetch delay to check deployment records from config * Update isCurrent after deployment record received --------- Co-authored-by: neeraj --- README.md | 4 +- packages/backend/environments/local.toml | 1 + packages/backend/package.json | 8 +- packages/backend/src/config.ts | 1 + packages/backend/src/database.ts | 23 +-- packages/backend/src/entity/Deployment.ts | 14 +- packages/backend/src/entity/Project.ts | 4 +- packages/backend/src/index.ts | 2 +- packages/backend/src/registry.ts | 33 ++-- packages/backend/src/resolvers.ts | 2 +- packages/backend/src/schema.gql | 2 +- packages/backend/src/service.ts | 151 ++++++++++++++---- packages/backend/src/types.ts | 24 +++ .../backend/test/fixtures/deployments.json | 44 ++--- packages/backend/test/fixtures/projects.json | 20 +-- .../backend/test/publish-deploy-records.ts | 6 +- .../deployments/DeploymentDetailsCard.tsx | 6 +- .../deployments/DeploymentDialogBodyCard.tsx | 8 +- packages/gql-client/src/types.ts | 2 +- 19 files changed, 252 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 3accec13..c46cbbb3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ - Load fixtures in database ```bash - yarn db:load:fixtures + yarn test:db:load:fixtures ``` - Set `gitHub.oAuth.clientId` and `gitHub.oAuth.clientSecret` in backend [config file](packages/backend/environments/local.toml) @@ -66,7 +66,7 @@ - Run the script to create bond, reserve the authority and set authority bond ```bash - yarn registry:init + yarn test:registry:init # snowball:initialize-registry bondId: 6af0ab81973b93d3511ae79841756fb5da3fd2f70ea1279e81fae7c9b19af6c4 +0ms ``` diff --git a/packages/backend/environments/local.toml b/packages/backend/environments/local.toml index 7ca5521f..b5b5d03a 100644 --- a/packages/backend/environments/local.toml +++ b/packages/backend/environments/local.toml @@ -13,6 +13,7 @@ clientSecret = "" [registryConfig] + fetchDeploymentRecordDelay = 5000 restEndpoint = "http://localhost:1317" gqlEndpoint = "http://localhost:9473/api" chainId = "laconic_9000-1" diff --git a/packages/backend/package.json b/packages/backend/package.json index c28f72c7..83138f1d 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -36,10 +36,10 @@ "lint": "eslint .", "format": "prettier --write .", "format:check": "prettier --check .", - "registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts", - "registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts", - "db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts", - "db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts" + "test:registry:init": "DEBUG=snowball:* ts-node ./test/initialize-registry.ts", + "test:registry:publish-deploy-records": "DEBUG=snowball:* ts-node ./test/publish-deploy-records.ts", + "test:db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts", + "test:db:delete": "DEBUG=snowball:* ts-node ./test/delete-db.ts" }, "devDependencies": { "@types/fs-extra": "^11.0.4", diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 50b34145..1a98a657 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -22,6 +22,7 @@ export interface RegistryConfig { chainId: string; privateKey: string; bondId: string; + fetchDeploymentRecordDelay: number; fee: { amount: string; denom: string; diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index a2959773..7c76f928 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -126,8 +126,18 @@ export class Database { return projects; } + /** + * Get deployments with specified filter + */ + async getDeployments (options: FindManyOptions): Promise { + const deploymentRepository = this.dataSource.getRepository(Deployment); + const deployments = await deploymentRepository.find(options); + + return deployments; + } + async getDeploymentsByProjectId (projectId: string): Promise { - const deployments = await this.getDeployments({ + return this.getDeployments({ relations: { project: true, domain: true, @@ -142,15 +152,6 @@ export class Database { createdAt: 'DESC' } }); - - return deployments; - } - - async getDeployments (options: FindManyOptions): Promise { - const deploymentRepository = this.dataSource.getRepository(Deployment); - const deployments = await deploymentRepository.find(options); - - return deployments; } async getDeployment (options: FindOneOptions): Promise { @@ -167,7 +168,7 @@ export class Database { return domains; } - async addDeployement (data: DeepPartial): Promise { + async addDeployment (data: DeepPartial): Promise { const deploymentRepository = this.dataSource.getRepository(Deployment); const id = nanoid(); diff --git a/packages/backend/src/entity/Deployment.ts b/packages/backend/src/entity/Deployment.ts index 76238f32..62d3f38f 100644 --- a/packages/backend/src/entity/Deployment.ts +++ b/packages/backend/src/entity/Deployment.ts @@ -12,6 +12,7 @@ import { import { Project } from './Project'; import { Domain } from './Domain'; import { User } from './User'; +import { AppDeploymentRecordAttributes } from '../types'; export enum Environment { Production = 'Production', @@ -45,6 +46,9 @@ export class Deployment { @PrimaryColumn('varchar') id!: string; + @Column() + projectId!: string; + @ManyToOne(() => Project, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'projectId' }) project!: Project; @@ -69,10 +73,16 @@ export class Deployment { url!: string | null; @Column('varchar') - registryRecordId!: string; + applicationRecordId!: string; @Column('simple-json') - registryRecordData!: ApplicationRecord; + applicationRecordData!: ApplicationRecord; + + @Column('varchar', { nullable: true }) + applicationDeploymentRecordId!: string | null; + + @Column('simple-json', { nullable: true }) + applicationDeploymentRecordData!: AppDeploymentRecordAttributes | null; @Column({ enum: Environment diff --git a/packages/backend/src/entity/Project.ts b/packages/backend/src/entity/Project.ts index 667d120c..86fa7b3d 100644 --- a/packages/backend/src/entity/Project.ts +++ b/packages/backend/src/entity/Project.ts @@ -53,10 +53,10 @@ export class Project { prodBranch!: string; @Column('varchar', { nullable: true }) - registryRecordId!: string | null; + applicationDeploymentRequestId!: string | null; @Column('simple-json', { nullable: true }) - registryRecordData!: ApplicationDeploymentRequest | null; + applicationDeploymentRequestData!: ApplicationDeploymentRequest | null; @Column('text', { default: '' }) description!: string; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index d29f3c32..7a871a28 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -31,7 +31,7 @@ export const main = async (): Promise => { await db.init(); const registry = new Registry(registryConfig); - const service = new Service({ gitHubConfig: gitHub }, db, app, registry); + const service = new Service({ gitHubConfig: gitHub, registryConfig }, db, app, registry); const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); const resolvers = await createResolvers(service); diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 02dc924b..108318bb 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -7,13 +7,14 @@ import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk'; import { RegistryConfig } from './config'; import { ApplicationDeploymentRequest } from './entity/Project'; -import { ApplicationRecord } from './entity/Deployment'; -import { PackageJSON } from './types'; +import { ApplicationRecord, Deployment } from './entity/Deployment'; +import { AppDeploymentRecord, PackageJSON } from './types'; const log = debug('snowball:registry'); const APP_RECORD_TYPE = 'ApplicationRecord'; -const DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRequest'; +const APP_DEPLOYMENT_REQUEST_TYPE = 'ApplicationDeploymentRequest'; +const APP_DEPLOYMENT_RECORD_TYPE = 'ApplicationDeploymentRecord'; // TODO: Move registry code to laconic-sdk/watcher-ts export class Registry { @@ -35,7 +36,7 @@ export class Registry { commitHash: string, appType: string, repoUrl: string - }): Promise<{registryRecordId: string, registryRecordData: ApplicationRecord}> { + }): Promise<{applicationRecordId: string, applicationRecordData: ApplicationRecord}> { assert(packageJSON.name, "name field doesn't exist in package.json"); // Use laconic-sdk to publish record // Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts/publish-app-record.sh @@ -87,7 +88,7 @@ export class Registry { await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.app_version}` }, this.registryConfig.privateKey, this.registryConfig.fee); await this.registry.setName({ cid: result.data.id, crn: `${crn}@${applicationRecord.repository_ref}` }, this.registryConfig.privateKey, this.registryConfig.fee); - return { registryRecordId: result.data.id, registryRecordData: applicationRecord }; + return { applicationRecordId: result.data.id, applicationRecordData: applicationRecord }; } async createApplicationDeploymentRequest (data: { @@ -96,8 +97,8 @@ export class Registry { repository: string, environmentVariables: { [key: string]: string } }): Promise<{ - registryRecordId: string, - registryRecordData: ApplicationDeploymentRequest + applicationDeploymentRequestId: string, + applicationDeploymentRequestData: ApplicationDeploymentRequest }> { const crn = this.getCrn(data.appName); const records = await this.registry.resolveNames([crn]); @@ -109,7 +110,7 @@ export class Registry { // Create record of type ApplicationDeploymentRequest and publish const applicationDeploymentRequest = { - type: DEPLOYMENT_RECORD_TYPE, + type: APP_DEPLOYMENT_REQUEST_TYPE, version: '1.0.0', name: `${applicationRecord.attributes.name}@${applicationRecord.attributes.app_version}`, application: `${crn}@${applicationRecord.attributes.app_version}`, @@ -141,7 +142,21 @@ export class Registry { log(`Application deployment request record published: ${result.data.id}`); log('Application deployment request data:', applicationDeploymentRequest); - return { registryRecordId: result.data.id, registryRecordData: applicationDeploymentRequest }; + return { applicationDeploymentRequestId: result.data.id, applicationDeploymentRequestData: applicationDeploymentRequest }; + } + + /** + * Fetch ApplicationDeploymentRecords for deployments + */ + async getDeploymentRecords (deployments: Deployment[]): Promise { + // Fetch ApplicationDeploymentRecords for corresponding ApplicationRecord set in deployments + // TODO: Implement Laconicd GQL query to filter records by multiple values for an attribute + const records = await this.registry.queryRecords({ + type: APP_DEPLOYMENT_RECORD_TYPE + }, true); + + // Filter records with ApplicationRecord ids + return records.filter((record: AppDeploymentRecord) => deployments.some(deployment => deployment.applicationRecordId === record.attributes.application)); } getCrn (packageJsonName: string): string { diff --git a/packages/backend/src/resolvers.ts b/packages/backend/src/resolvers.ts index 02602aae..f583bdab 100644 --- a/packages/backend/src/resolvers.ts +++ b/packages/backend/src/resolvers.ts @@ -30,7 +30,7 @@ export const createResolvers = async (service: Service): Promise => { }, deployments: async (_: any, { projectId }: { projectId: string }) => { - return service.getDeployementsByProjectId(projectId); + return service.getDeploymentsByProjectId(projectId); }, environmentVariables: async (_: any, { projectId }: { projectId: string }) => { diff --git a/packages/backend/src/schema.gql b/packages/backend/src/schema.gql index 8d048078..620dec72 100644 --- a/packages/backend/src/schema.gql +++ b/packages/backend/src/schema.gql @@ -91,7 +91,7 @@ type Deployment { branch: String! commitHash: String! commitMessage: String! - url: String! + url: String environment: Environment! isCurrent: Boolean! status: DeploymentStatus! diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 8edb63a0..b2ba8fc1 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -14,14 +14,16 @@ import { Project } from './entity/Project'; import { Permission, ProjectMember } from './entity/ProjectMember'; import { User } from './entity/User'; import { Registry } from './registry'; -import { GitHubConfig } from './config'; -import { GitPushEventPayload } from './types'; +import { GitHubConfig, RegistryConfig } from './config'; +import { AppDeploymentRecord, GitPushEventPayload } from './types'; const log = debug('snowball:service'); + const GITHUB_UNIQUE_WEBHOOK_ERROR = 'Hook already exists on this repository'; interface Config { gitHubConfig: GitHubConfig + registryConfig: RegistryConfig } export class Service { @@ -30,11 +32,113 @@ export class Service { private registry: Registry; private config: Config; + private deployRecordCheckTimeout?: NodeJS.Timeout; + constructor (config: Config, db: Database, app: OAuthApp, registry: Registry) { this.db = db; this.oauthApp = app; this.registry = registry; this.config = config; + this.init(); + } + + /** + * Initialize services + */ + init (): void { + // Start check for ApplicationDeploymentRecords asynchronously + this.checkDeployRecordsAndUpdate(); + } + + /** + * Destroy services + */ + destroy (): void { + clearTimeout(this.deployRecordCheckTimeout); + } + + /** + * Checks for ApplicationDeploymentRecord and update corresponding deployments + * Continues check in loop after a delay of DEPLOY_RECORD_CHECK_DELAY_MS + */ + async checkDeployRecordsAndUpdate (): Promise { + // Fetch deployments in building state + const deployments = await this.db.getDeployments({ + where: { + status: DeploymentStatus.Building + // TODO: Fetch and check records for recent deployments + } + }); + + if (deployments.length) { + log(`Found ${deployments.length} deployments in ${DeploymentStatus.Building} state`); + + // Fetch ApplicationDeploymentRecord for deployments + const records = await this.registry.getDeploymentRecords(deployments); + log(`Found ${records.length} ApplicationDeploymentRecords`); + + // Update deployments for which ApplicationDeploymentRecords were returned + await this.updateDeploymentsWithRecordData(records); + } + + this.deployRecordCheckTimeout = setTimeout(() => { + this.checkDeployRecordsAndUpdate(); + }, this.config.registryConfig.fetchDeploymentRecordDelay); + } + + /** + * Update deployments with ApplicationDeploymentRecord data + */ + async updateDeploymentsWithRecordData (records: AppDeploymentRecord[]): Promise { + // Get deployments for ApplicationDeploymentRecords + const deployments = await this.db.getDeployments({ + where: records.map(record => ({ + applicationRecordId: record.attributes.application + })), + order: { + createdAt: 'DESC' + } + }); + + // Get project IDs of deployments that are in production environment + const productionDeploymentProjectIds = deployments.reduce((acc, deployment): Set => { + if (deployment.environment === Environment.Production) { + acc.add(deployment.projectId); + } + + return acc; + }, new Set()); + + // Set old deployments isCurrent to false + await Promise.all(Array.from(productionDeploymentProjectIds).map( + // TODO: Add DB method to update multiple deployments in single query + async (projectId) => this.db.updateDeployment({ projectId }, { isCurrent: false }) + )); + + const recordToDeploymentsMap = deployments.reduce((acc: {[key: string]: Deployment}, deployment) => { + acc[deployment.applicationRecordId] = deployment; + return acc; + }, {}); + + // Update deployment data for ApplicationDeploymentRecords + const deploymentUpdatePromises = records.map(async (record) => { + const deployment = recordToDeploymentsMap[record.attributes.application]; + + await this.db.updateDeploymentById( + deployment.id, + { + applicationDeploymentRecordId: record.id, + applicationDeploymentRecordData: record.attributes, + url: record.attributes.url, + status: DeploymentStatus.Ready, + isCurrent: deployment.environment === Environment.Production + } + ); + + log(`Updated deployment deployment ${deployment.id} with URL ${record.attributes.url}`); + }); + + await Promise.all(deploymentUpdatePromises); } async getUser (userId: string): Promise { @@ -67,7 +171,7 @@ export class Service { return dbProjects; } - async getDeployementsByProjectId (projectId: string): Promise { + async getDeploymentsByProjectId (projectId: string): Promise { const dbDeployments = await this.db.getDeploymentsByProjectId(projectId); return dbDeployments; } @@ -187,7 +291,7 @@ export class Service { const octokit = await this.getOctokit(userId); - const newDeployement = await this.createDeployment(userId, + const newDeployment = await this.createDeployment(userId, octokit, { project: oldDeployment.project, @@ -199,7 +303,7 @@ export class Service { commitMessage: oldDeployment.commitMessage }); - return newDeployement; + return newDeployment; } async createDeployment ( @@ -232,24 +336,13 @@ export class Service { } // TODO: Set environment variables for each deployment (environment variables can`t be set in application record) - const { registryRecordId, registryRecordData } = await this.registry.createApplicationRecord({ + const { applicationRecordId, applicationRecordData } = await this.registry.createApplicationRecord({ packageJSON, appType: data.project!.template!, commitHash: data.commitHash!, repoUrl: recordData.repoUrl }); - // Check if new deployment is set to current - if (data.isCurrent) { - // Update previous current deployment - await this.db.updateDeployment({ - project: { - id: data.project.id - }, - isCurrent: true - }, { isCurrent: false }); - } - // Update previous deployment with prod branch domain // TODO: Fix unique constraint error for domain await this.db.updateDeployment({ @@ -258,7 +351,7 @@ export class Service { domain: null }); - const newDeployement = await this.db.addDeployement({ + const newDeployment = await this.db.addDeployment({ project: data.project, branch: data.branch, commitHash: data.commitHash, @@ -266,16 +359,16 @@ export class Service { environment: data.environment, isCurrent: data.isCurrent, status: DeploymentStatus.Building, - registryRecordId, - registryRecordData, + applicationRecordId, + applicationRecordData, domain: data.domain, createdBy: Object.assign(new User(), { id: userId }) }); - log(`Created deployment ${newDeployement.id} and published application record ${registryRecordId}`); - return newDeployement; + log(`Created deployment ${newDeployment.id} and published application record ${applicationRecordId}`); + return newDeployment; } async addProject (userId: string, organizationSlug: string, data: DeepPartial): Promise { @@ -327,17 +420,17 @@ export class Service { return acc; }, {} as { [key: string]: string }); - const { registryRecordId, registryRecordData } = await this.registry.createApplicationDeploymentRequest( + const { applicationDeploymentRequestId, applicationDeploymentRequestData } = await this.registry.createApplicationDeploymentRequest( { - appName: newDeployment.registryRecordData.name!, + appName: newDeployment.applicationRecordData.name!, commitHash: latestCommit.sha, repository: repoDetails.html_url, environmentVariables: environmentVariablesObj }); await this.db.updateProjectById(project.id, { - registryRecordId, - registryRecordData + applicationDeploymentRequestId, + applicationDeploymentRequestData }); await this.createRepoHook(octokit, project); @@ -444,7 +537,7 @@ export class Service { const octokit = await this.getOctokit(userId); - const newDeployement = await this.createDeployment(userId, + const newDeployment = await this.createDeployment(userId, octokit, { project: oldDeployment.project, @@ -457,7 +550,7 @@ export class Service { commitMessage: oldDeployment.commitMessage }); - return newDeployement; + return newDeployment; } async rollbackDeployment (projectId: string, deploymentId: string): Promise { @@ -475,7 +568,7 @@ export class Service { }); if (!oldCurrentDeployment) { - throw new Error('Current deployement doesnot exist'); + throw new Error('Current deployment doesnot exist'); } const oldCurrentDeploymentUpdate = await this.db.updateDeploymentById(oldCurrentDeployment.id, { isCurrent: false, domain: null }); diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 9fb154cb..71626645 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -25,3 +25,27 @@ export interface GitPushEventPayload { message: string; }; } + +export interface AppDeploymentRecordAttributes { + application: string; + dns: string; + meta: string; + name: string; + request: string; + type: string; + url: string; + version: string; +} + +interface RegistryRecord { + id: string; + names: string[] | null; + owners: string[]; + bondId: string; + createTime: string; + expiryTime: string; +} + +export interface AppDeploymentRecord extends RegistryRecord { + attributes: AppDeploymentRecordAttributes +} diff --git a/packages/backend/test/fixtures/deployments.json b/packages/backend/test/fixtures/deployments.json index 25176713..5eb682a8 100644 --- a/packages/backend/test/fixtures/deployments.json +++ b/packages/backend/test/fixtures/deployments.json @@ -7,8 +7,8 @@ "status": "Ready", "environment": "Production", "isCurrent": true, - "registryRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -22,8 +22,8 @@ "status": "Ready", "environment": "Preview", "isCurrent": false, - "registryRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -37,8 +37,8 @@ "status": "Ready", "environment": "Development", "isCurrent": false, - "registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -52,8 +52,8 @@ "status": "Ready", "environment": "Production", "isCurrent": false, - "registryRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "prod", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -67,8 +67,8 @@ "status": "Ready", "environment": "Production", "isCurrent": true, - "registryRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -82,8 +82,8 @@ "status": "Ready", "environment": "Preview", "isCurrent": false, - "registryRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -97,8 +97,8 @@ "status": "Ready", "environment": "Development", "isCurrent": false, - "registryRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -112,8 +112,8 @@ "status": "Ready", "environment": "Production", "isCurrent": true, - "registryRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -127,8 +127,8 @@ "status": "Ready", "environment": "Preview", "isCurrent": false, - "registryRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -142,8 +142,8 @@ "status": "Ready", "environment": "Development", "isCurrent": false, - "registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", - "registryRecordData": {}, + "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -157,8 +157,8 @@ "status": "Ready", "environment": "Production", "isCurrent": true, - "registryRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", - "registryRecordData": {}, + "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", + "applicationRecordData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", diff --git a/packages/backend/test/fixtures/projects.json b/packages/backend/test/fixtures/projects.json index f2def3f2..55c5ee23 100644 --- a/packages/backend/test/fixtures/projects.json +++ b/packages/backend/test/fixtures/projects.json @@ -10,8 +10,8 @@ "framework": "test", "webhooks": [], "icon": "", - "registryRecordId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationDeploymentRequestId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "subDomain": "testProject.snowball.xyz" }, { @@ -25,8 +25,8 @@ "framework": "test-2", "webhooks": [], "icon": "", - "registryRecordId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationDeploymentRequestId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "subDomain": "testProject-2.snowball.xyz" }, { @@ -40,8 +40,8 @@ "framework": "test-3", "webhooks": [], "icon": "", - "registryRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationDeploymentRequestId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "subDomain": "iglootools.snowball.xyz" }, { @@ -55,8 +55,8 @@ "framework": "test-4", "webhooks": [], "icon": "", - "registryRecordId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationDeploymentRequestId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "subDomain": "iglootools-2.snowball.xyz" }, { @@ -70,8 +70,8 @@ "framework": "test-5", "webhooks": [], "icon": "", - "registryRecordId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "registryRecordData": {}, + "applicationDeploymentRequestId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "subDomain": "snowball-2.snowball.xyz" } ] diff --git a/packages/backend/test/publish-deploy-records.ts b/packages/backend/test/publish-deploy-records.ts index 6f2d26b0..f5636c4e 100644 --- a/packages/backend/test/publish-deploy-records.ts +++ b/packages/backend/test/publish-deploy-records.ts @@ -41,8 +41,8 @@ async function main () { const applicationDeploymentRecord = { type: 'ApplicationDeploymentRecord', version: '0.0.1', - name: deployment.registryRecordData.name, - application: deployment.registryRecordId, + name: deployment.applicationRecordData.name, + application: deployment.applicationRecordId, // TODO: Create DNS record dns: 'bafyreihlymqggsgqiqawvehkpr2imt4l3u6q7um7xzjrux5rhsvwnuyewm', @@ -53,7 +53,7 @@ async function main () { so: '66fcfa49a1664d4cb4ce4f72c1c0e151' }), - request: deployment.project.registryRecordId, + request: deployment.project.applicationDeploymentRequestId, url }; diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx index 5add83f9..1121eb54 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx @@ -90,7 +90,9 @@ const DeploymentDetailsCard = ({
- {deployment.url} + {deployment.url && ( + {deployment.url} + )} - ^ Visit + ^ Visit setAssignDomainDialog(!assignDomainDialog)} diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx index 65a5a4b6..e4388bf8 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDialogBodyCard.tsx @@ -28,9 +28,11 @@ const DeploymentDialogBodyCard = ({ color={chip.color} /> )} - - {deployment.url} - + {deployment.url && ( + + {deployment.url} + + )} ^ {deployment.branch} ^{' '} {deployment.commitHash.substring(0, SHORT_COMMIT_HASH_LENGTH)}{' '} diff --git a/packages/gql-client/src/types.ts b/packages/gql-client/src/types.ts index e75d0aba..71c44c00 100644 --- a/packages/gql-client/src/types.ts +++ b/packages/gql-client/src/types.ts @@ -63,7 +63,7 @@ export type Deployment = { branch: string commitHash: string commitMessage: string - url: string + url?: string environment: Environment isCurrent: boolean status: DeploymentStatus From c3d1b4f3eb226f475a4bdaebfd156e85b0672c51 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Mon, 19 Feb 2024 16:55:07 +0530 Subject: [PATCH 3/8] Use branches from GitHub API in edit domain dialog (#74) * Use branches from GitHub API * Disable git branch input if repo not found * Disable git branch if branches is empty * Use async select for accounts dropdown * Log actual HTTP error in console --------- Co-authored-by: neeraj --- .../projects/create/RepositoryList.tsx | 8 ++--- .../projects/project/settings/DomainCard.tsx | 7 ++-- .../project/settings/EditDomainDialog.tsx | 15 ++++---- .../frontend/src/context/OctokitContext.tsx | 4 +-- .../pages/org-slug/projects/create/index.tsx | 2 +- .../pages/org-slug/projects/id/Overview.tsx | 4 --- .../org-slug/projects/id/settings/Domains.tsx | 34 +++++++++++++++++-- packages/frontend/src/types.ts | 9 ----- 8 files changed, 50 insertions(+), 33 deletions(-) diff --git a/packages/frontend/src/components/projects/create/RepositoryList.tsx b/packages/frontend/src/components/projects/create/RepositoryList.tsx index ad69e776..d0f360bb 100644 --- a/packages/frontend/src/components/projects/create/RepositoryList.tsx +++ b/packages/frontend/src/components/projects/create/RepositoryList.tsx @@ -3,11 +3,12 @@ import { Octokit } from 'octokit'; import assert from 'assert'; import { useDebounce } from 'usehooks-ts'; -import { Button, Typography, Option, Select } from '@material-tailwind/react'; +import { Button, Typography, Option } from '@material-tailwind/react'; import SearchBar from '../../SearchBar'; import ProjectRepoCard from './ProjectRepoCard'; import { GitOrgDetails, GitRepositoryDetails } from '../../../types'; +import AsyncSelect from '../../shared/AsyncSelect'; const DEFAULT_SEARCHED_REPO = ''; const REPOS_PER_PAGE = 5; @@ -109,8 +110,7 @@ const RepositoryList = ({ octokit }: RepositoryListProps) => {
- {/* TODO: Fix selection of Git user at start */} - +
Promise; } @@ -44,7 +43,7 @@ const DOMAIN_RECORD = { const DomainCard = ({ domains, domain, - repo, + branches, project, onUpdate, }: DomainCardProps) => { @@ -188,7 +187,7 @@ const DomainCard = ({ domains={domains} open={editDialogOpen} domain={domain} - repo={repo} + branches={branches} onUpdate={onUpdate} /> diff --git a/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx b/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx index 78e4aceb..b9d2866c 100644 --- a/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx +++ b/packages/frontend/src/components/projects/project/settings/EditDomainDialog.tsx @@ -15,7 +15,6 @@ import { Option, } from '@material-tailwind/react'; -import { RepositoryDetails } from '../../../../types'; import { useGQLClient } from '../../../../context/GQLClientContext'; const DEFAULT_REDIRECT_OPTIONS = ['none']; @@ -25,7 +24,7 @@ interface EditDomainDialogProp { open: boolean; handleOpen: () => void; domain: Domain; - repo: RepositoryDetails; + branches: string[]; onUpdate: () => Promise; } @@ -40,7 +39,7 @@ const EditDomainDialog = ({ open, handleOpen, domain, - repo, + branches, onUpdate, }: EditDomainDialogProp) => { const client = useGQLClient(); @@ -120,7 +119,7 @@ const EditDomainDialog = ({ branch: domain.branch, redirectedTo: getRedirectUrl(domain), }); - }, [domain, repo]); + }, [domain]); return ( @@ -166,9 +165,13 @@ const EditDomainDialog = ({ repo.branch.includes(value), + validate: (value) => + Boolean(branches.length) ? branches.includes(value) : true, })} - disabled={watch('redirectedTo') !== DEFAULT_REDIRECT_OPTIONS[0]} + disabled={ + !Boolean(branches.length) || + watch('redirectedTo') !== DEFAULT_REDIRECT_OPTIONS[0] + } /> {!isValid && ( diff --git a/packages/frontend/src/context/OctokitContext.tsx b/packages/frontend/src/context/OctokitContext.tsx index 0eed37ce..21ffe246 100644 --- a/packages/frontend/src/context/OctokitContext.tsx +++ b/packages/frontend/src/context/OctokitContext.tsx @@ -14,13 +14,13 @@ import { useGQLClient } from './GQLClientContext'; const UNAUTHORIZED_ERROR_CODE = 401; interface ContextValue { - octokit: Octokit | null; + octokit: Octokit; isAuth: boolean; updateAuth: () => void; } const OctokitContext = createContext({ - octokit: null, + octokit: new Octokit(), isAuth: false, updateAuth: () => {}, }); diff --git a/packages/frontend/src/pages/org-slug/projects/create/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/index.tsx index 8658d18c..a48f4dd4 100644 --- a/packages/frontend/src/pages/org-slug/projects/create/index.tsx +++ b/packages/frontend/src/pages/org-slug/projects/create/index.tsx @@ -24,7 +24,7 @@ const NewProject = () => { })}
Import a repository
- + ) : ( diff --git a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx index c5db5c34..a5f1ad73 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Overview.tsx @@ -24,10 +24,6 @@ const OverviewTabPanel = () => { const { project } = useOutletContext(); useEffect(() => { - if (!octokit) { - return; - } - // TODO: Save repo commits in DB and avoid using GitHub API in frontend // TODO: Figure out fetching latest commits for all branches const fetchRepoActivity = async () => { diff --git a/packages/frontend/src/pages/org-slug/projects/id/settings/Domains.tsx b/packages/frontend/src/pages/org-slug/projects/id/settings/Domains.tsx index f912e8fa..24457b1a 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/settings/Domains.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/settings/Domains.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react'; +import { RequestError } from 'octokit'; +import React, { useCallback, useEffect, useState } from 'react'; import { Link, useOutletContext } from 'react-router-dom'; import { Domain } from 'gql-client'; @@ -6,14 +7,41 @@ import { Button, Typography } from '@material-tailwind/react'; import DomainCard from '../../../../../components/projects/project/settings/DomainCard'; import { useGQLClient } from '../../../../../context/GQLClientContext'; -import repositories from '../../../../../assets/repositories.json'; import { OutletContextType } from '../../../../../types'; +import { useOctokit } from '../../../../../context/OctokitContext'; const Domains = () => { const client = useGQLClient(); + const { octokit } = useOctokit(); const { project } = useOutletContext(); const [domains, setDomains] = useState([]); + const [branches, setBranches] = useState([]); + + const fetchBranches = useCallback(async () => { + const [owner, repo] = project.repository.split('/'); + + try { + const result = await octokit.rest.repos.listBranches({ + owner, + repo, + }); + + const branches = result.data.map((repo) => repo.name); + + setBranches(branches); + } catch (err) { + if (!(err instanceof RequestError && err.status === 404)) { + throw err; + } + + console.error(err); + } + }, []); + + useEffect(() => { + fetchBranches(); + }, []); const fetchDomains = async () => { if (project === undefined) { @@ -46,7 +74,7 @@ const Domains = () => { domain={domain} key={domain.id} // TODO: Use github API for getting linked repository - repo={repositories[0]!} + branches={branches} project={project} onUpdate={fetchDomains} /> diff --git a/packages/frontend/src/types.ts b/packages/frontend/src/types.ts index 1ece61d3..2ca22da6 100644 --- a/packages/frontend/src/types.ts +++ b/packages/frontend/src/types.ts @@ -6,15 +6,6 @@ export interface GitOrgDetails { avatar_url: string; } -// TODO: Use GitRepositoryDetails -export interface RepositoryDetails { - title: string; - updatedAt: string; - user: string; - private: boolean; - branch: string[]; -} - export interface GitRepositoryDetails { id: number; name: string; From a45fb4c6175ca9dcc94bad23035c912b1f18f9f1 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Tue, 20 Feb 2024 10:53:42 +0530 Subject: [PATCH 4/8] Fix deployments visit URL and date filter (#78) * Add method to update multiple deployments in single query * Fix deployments URL visit and date filter * Clean fixtures data * Update fixtures data --------- Co-authored-by: neeraj --- packages/backend/src/database.ts | 13 ++ packages/backend/src/service.ts | 16 +- .../backend/test/fixtures/organizations.json | 4 +- .../test/fixtures/primary-domains.json | 12 +- .../test/fixtures/redirected-domains.json | 6 +- ...nizations.json => user-organizations.json} | 0 packages/backend/test/fixtures/users.json | 12 +- packages/backend/test/initialize-db.ts | 2 +- packages/frontend/src/assets/domains.json | 6 +- packages/frontend/src/assets/members.json | 17 -- .../frontend/src/assets/process-logs.json | 2 +- packages/frontend/src/assets/projects.json | 190 ------------------ .../deployments/DeploymentDetailsCard.tsx | 6 +- .../org-slug/projects/id/Deployments.tsx | 6 +- 14 files changed, 45 insertions(+), 247 deletions(-) rename packages/backend/test/fixtures/{user-orgnizations.json => user-organizations.json} (100%) delete mode 100644 packages/frontend/src/assets/members.json delete mode 100644 packages/frontend/src/assets/projects.json diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index 7c76f928..a45c354d 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -316,6 +316,19 @@ export class Database { return Boolean(updateResult.affected); } + async updateDeploymentsByProjectIds (projectIds: string[], data: DeepPartial): Promise { + const deploymentRepository = this.dataSource.getRepository(Deployment); + + const updateResult = await deploymentRepository + .createQueryBuilder() + .update(Deployment) + .set(data) + .where('projectId IN (:...projectIds)', { projectIds }) + .execute(); + + return Boolean(updateResult.affected); + } + async addProject (userId: string, organizationId: string, data: DeepPartial): Promise { const projectRepository = this.dataSource.getRepository(Project); diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index b2ba8fc1..0ce326ff 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -78,7 +78,9 @@ export class Service { log(`Found ${records.length} ApplicationDeploymentRecords`); // Update deployments for which ApplicationDeploymentRecords were returned - await this.updateDeploymentsWithRecordData(records); + if (records.length) { + await this.updateDeploymentsWithRecordData(records); + } } this.deployRecordCheckTimeout = setTimeout(() => { @@ -110,10 +112,7 @@ export class Service { }, new Set()); // Set old deployments isCurrent to false - await Promise.all(Array.from(productionDeploymentProjectIds).map( - // TODO: Add DB method to update multiple deployments in single query - async (projectId) => this.db.updateDeployment({ projectId }, { isCurrent: false }) - )); + await this.db.updateDeploymentsByProjectIds(Array.from(productionDeploymentProjectIds), { isCurrent: false }); const recordToDeploymentsMap = deployments.reduce((acc: {[key: string]: Deployment}, deployment) => { acc[deployment.applicationRecordId] = deployment; @@ -135,7 +134,7 @@ export class Service { } ); - log(`Updated deployment deployment ${deployment.id} with URL ${record.attributes.url}`); + log(`Updated deployment ${deployment.id} with URL ${record.attributes.url}`); }); await Promise.all(deploymentUpdatePromises); @@ -295,7 +294,6 @@ export class Service { octokit, { project: oldDeployment.project, - isCurrent: true, branch: oldDeployment.branch, environment: Environment.Production, domain: prodBranchDomains[0], @@ -357,7 +355,6 @@ export class Service { commitHash: data.commitHash, commitMessage: data.commitMessage, environment: data.environment, - isCurrent: data.isCurrent, status: DeploymentStatus.Building, applicationRecordId, applicationRecordData, @@ -400,7 +397,6 @@ export class Service { octokit, { project, - isCurrent: true, branch: project.prodBranch, environment: Environment.Production, domain: null, @@ -486,7 +482,6 @@ export class Service { octokit, { project, - isCurrent: project.prodBranch === branch, branch, environment: project.prodBranch === branch ? Environment.Production : Environment.Preview, domain, @@ -543,7 +538,6 @@ export class Service { project: oldDeployment.project, // TODO: Put isCurrent field in project branch: oldDeployment.branch, - isCurrent: true, environment: Environment.Production, domain: oldDeployment.domain, commitHash: oldDeployment.commitHash, diff --git a/packages/backend/test/fixtures/organizations.json b/packages/backend/test/fixtures/organizations.json index bf934e81..ace516d3 100644 --- a/packages/backend/test/fixtures/organizations.json +++ b/packages/backend/test/fixtures/organizations.json @@ -6,7 +6,7 @@ }, { "id": "7eb9b3eb-eb74-4b53-b59a-69884c82a7fb", - "name": "AirFoil", - "slug": "airfoil-2" + "name": "Laconic", + "slug": "laconic-2" } ] diff --git a/packages/backend/test/fixtures/primary-domains.json b/packages/backend/test/fixtures/primary-domains.json index b1cb0a99..23e2666d 100644 --- a/packages/backend/test/fixtures/primary-domains.json +++ b/packages/backend/test/fixtures/primary-domains.json @@ -1,37 +1,37 @@ [ { "projectIndex": 0, - "name": "randomurl.snowballtools.xyz", + "name": "example.snowballtools.xyz", "status": "Live", "branch": "main" }, { "projectIndex": 0, - "name": "saugatt.com", + "name": "example.org", "status": "Pending", "branch": "test" }, { "projectIndex": 1, - "name": "randomurl.snowballtools.xyz", + "name": "example.snowballtools.xyz", "status": "Live", "branch": "main" }, { "projectIndex": 1, - "name": "saugatt.com", + "name": "example.org", "status": "Pending", "branch": "test" }, { "projectIndex": 2, - "name": "randomurl.snowballtools.xyz", + "name": "example.snowballtools.xyz", "status": "Live", "branch": "main" }, { "projectIndex": 2, - "name": "saugatt.com", + "name": "example.org", "status": "Pending", "branch": "test" }, diff --git a/packages/backend/test/fixtures/redirected-domains.json b/packages/backend/test/fixtures/redirected-domains.json index d526bde3..64fa9736 100644 --- a/packages/backend/test/fixtures/redirected-domains.json +++ b/packages/backend/test/fixtures/redirected-domains.json @@ -1,21 +1,21 @@ [ { "projectIndex": 0, - "name": "www.saugatt.com", + "name": "www.example.org", "status": "Pending", "redirectToIndex": 1, "branch": "test" }, { "projectIndex": 1, - "name": "www.saugatt.com", + "name": "www.example.org", "status": "Pending", "redirectToIndex": 3, "branch": "test" }, { "projectIndex": 2, - "name": "www.saugatt.com", + "name": "www.example.org", "status": "Pending", "redirectToIndex": 5, "branch": "test" diff --git a/packages/backend/test/fixtures/user-orgnizations.json b/packages/backend/test/fixtures/user-organizations.json similarity index 100% rename from packages/backend/test/fixtures/user-orgnizations.json rename to packages/backend/test/fixtures/user-organizations.json diff --git a/packages/backend/test/fixtures/users.json b/packages/backend/test/fixtures/users.json index aacd7b6b..118beba5 100644 --- a/packages/backend/test/fixtures/users.json +++ b/packages/backend/test/fixtures/users.json @@ -1,20 +1,20 @@ [ { "id": "59f4355d-9549-4aac-9b54-eeefceeabef0", - "name": "Saugat Yadav", - "email": "saugaty@airfoil.studio", + "name": "Snowball", + "email": "snowball@snowballtools.xyz", "isVerified": true }, { "id": "e505b212-8da6-48b2-9614-098225dab34b", - "name": "Gideon Low", - "email": "gideonl@airfoil.studio", + "name": "Alice Anderson", + "email": "alice@snowballtools.xyz", "isVerified": true }, { "id": "cd892fad-9138-4aa2-a62c-414a32776ea7", - "name": "Sushan Yadav", - "email": "sushany@airfoil.studio", + "name": "Bob Banner", + "email": "bob@snowballtools.xyz", "isVerified": true } ] diff --git a/packages/backend/test/initialize-db.ts b/packages/backend/test/initialize-db.ts index fdfeeedb..0aade587 100644 --- a/packages/backend/test/initialize-db.ts +++ b/packages/backend/test/initialize-db.ts @@ -20,7 +20,7 @@ const log = debug('snowball:initialize-database'); const USER_DATA_PATH = './fixtures/users.json'; const PROJECT_DATA_PATH = './fixtures/projects.json'; const ORGANIZATION_DATA_PATH = './fixtures/organizations.json'; -const USER_ORGANIZATION_DATA_PATH = './fixtures/user-orgnizations.json'; +const USER_ORGANIZATION_DATA_PATH = './fixtures/user-organizations.json'; const PROJECT_MEMBER_DATA_PATH = './fixtures/project-members.json'; const PRIMARY_DOMAIN_DATA_PATH = './fixtures/primary-domains.json'; const DEPLOYMENT_DATA_PATH = './fixtures/deployments.json'; diff --git a/packages/frontend/src/assets/domains.json b/packages/frontend/src/assets/domains.json index e17d1738..11020b61 100644 --- a/packages/frontend/src/assets/domains.json +++ b/packages/frontend/src/assets/domains.json @@ -2,7 +2,7 @@ { "id": 1, "projectid": 1, - "name": "randomurl.snowballtools.xyz", + "name": "example.snowballtools.xyz", "status": "live", "record": null, "isRedirectedto": false @@ -10,7 +10,7 @@ { "id": 2, "projectid": 1, - "name": "saugatt.com", + "name": "example.org", "status": "pending", "record": { "type": "A", @@ -22,7 +22,7 @@ { "id": 3, "projectid": 1, - "name": "www.saugatt.com", + "name": "www.example.org", "status": "pending", "record": { "type": "CNAME", diff --git a/packages/frontend/src/assets/members.json b/packages/frontend/src/assets/members.json deleted file mode 100644 index 6e38602c..00000000 --- a/packages/frontend/src/assets/members.json +++ /dev/null @@ -1,17 +0,0 @@ -[ - { - "name": "Saugat Yadav", - "email": "saugaty@airfoil.studio", - "id": 1 - }, - { - "name": "Gideon Low", - "email": "gideonl@airfoil.studio", - "id": 2 - }, - { - "name": "Sushan Yadav", - "email": "sushany@airfoil.studio", - "id": 3 - } -] diff --git a/packages/frontend/src/assets/process-logs.json b/packages/frontend/src/assets/process-logs.json index 34c7e24b..097a0fa9 100644 --- a/packages/frontend/src/assets/process-logs.json +++ b/packages/frontend/src/assets/process-logs.json @@ -1,6 +1,6 @@ [ "[20:50:03.502] Running build in Washington, D.C., USA (East) – iad1", - "[20:50:03.641] Cloning github.com/saugatyadav11/nextjs2 (Branch: main, Commit: 4a5f47f)", + "[20:50:03.641] Cloning github.com/cerc-io/nextjs2 (Branch: main, Commit: 4a5f47f)", "[20:50:04.004] Previous build cache not available", "[20:50:04.118] Cloning completed: 480.574ms", "[20:50:04.382] Running 'vercel build'", diff --git a/packages/frontend/src/assets/projects.json b/packages/frontend/src/assets/projects.json deleted file mode 100644 index a344fdf7..00000000 --- a/packages/frontend/src/assets/projects.json +++ /dev/null @@ -1,190 +0,0 @@ -[ - { - "id": 1, - "icon": "^", - "name": "iglootools", - "title": "Iglootools", - "domain": null, - "organization": "Airfoil", - "url": "iglootools.co", - "createdAt": "2023-12-07T04:20:00", - "createdBy": "Alice", - "deployment": "iglootools.snowballtools.co", - "source": "feature/add-remote-control", - "latestCommit": { - "message": "subscription added", - "createdAt": "2023-12-11T04:20:00", - "branch": "main" - }, - "repositoryId": 1, - "members": [ - { - "id": 1, - "permissions": [] - }, - { - "id": 2, - "permissions": ["view", "edit"] - }, - { - "id": 3, - "permissions": ["view"] - } - ], - "ownerId": 1 - }, - { - "id": 2, - "icon": "^", - "name": "snowball-starter-kit", - "title": "Snowball Starter Kit", - "domain": null, - "organization": "Snowball", - "url": "starterkit.snowballtools.com", - "createdAt": "2023-12-04T04:20:00", - "createdBy": "Bob", - "deployment": "deploy.snowballtools.com", - "source": "prod/add-docker-compose", - "latestCommit": { - "message": "component updates", - "createdAt": "2023-12-11T04:20:00", - "branch": "staging" - }, - "repositoryId": 1, - "members": [ - { - "id": 2, - "permissions": [] - }, - { - "id": 3, - "permissions": ["view"] - } - ], - "ownerId": 2 - }, - { - "id": 3, - "icon": "^", - "name": "web3-android", - "title": "Web3 Android", - "domain": null, - "organization": "Personal", - "url": "web3fordroids.com", - "createdAt": "2023-12-01T04:20:00", - "createdBy": "Charlie", - "deployment": "deploy.web3fordroids.com", - "source": "dev/style-page", - "latestCommit": { - "message": "No repo connected", - "createdAt": "2023-12-01T04:20:00", - "branch": "main" - }, - "repositoryId": 1, - "members": [ - { - "id": 1, - "permissions": [] - }, - { - "id": 2, - "permissions": ["view", "edit"] - }, - { - "id": 3, - "permissions": ["view"] - } - ], - "ownerId": 1 - }, - { - "id": 4, - "icon": "^", - "name": "passkeys-demo", - "title": "Passkeys Demo", - "domain": null, - "organization": "Airfoil", - "url": "passkeys.iglootools.xyz", - "createdAt": "2023-12-01T04:20:00", - "createdBy": "David", - "deployment": "demo.passkeys.xyz", - "source": "dev/style-page", - "latestCommit": { - "message": "hello world", - "createdAt": "2023-12-01T04:20:00", - "branch": "main" - }, - "repositoryId": 1, - "members": [ - { - "id": 1, - "permissions": [] - }, - { - "id": 2, - "permissions": ["view", "edit"] - }, - { - "id": 3, - "permissions": ["view"] - } - ], - "ownerId": 1 - }, - { - "id": 5, - "icon": "^", - "name": "iglootools", - "title": "Iglootools", - "domain": null, - "organization": "Airfoil", - "url": "iglootools.xyz", - "createdAt": "2023-12-11T04:20:00", - "createdBy": "Erin", - "deployment": "staging.snowballtools.com", - "source": "prod/fix-error", - "latestCommit": { - "message": "404 added", - "createdAt": "2023-12-09T04:20:00", - "branch": "main" - }, - "repositoryId": 1, - "members": [ - { - "id": 3, - "permissions": [] - } - ], - "ownerId": 3 - }, - { - "id": 6, - "icon": "^", - "name": "iglootools", - "title": "Iglootools", - "domain": null, - "organization": "Airfoil", - "url": "iglootools.xyz", - "createdAt": "2023-12-11T04:20:00", - "createdBy": "Frank", - "deployment": "iglootools.snowballtools.com", - "source": "prod/fix-error", - "latestCommit": { - "message": "design system integrated", - "createdAt": "2023-12-09T04:20:00", - "branch": "prod" - }, - "repositoryId": 1, - "members": [ - { - "id": 2, - "permissions": [] - }, - { - "id": 3, - "permissions": ["view"] - } - ], - "ownerId": 2 - } -] diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx index 1121eb54..60bc1237 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx @@ -122,11 +122,7 @@ const DeploymentDetailsCard = ({ - + ^ Visit { const dateMatch = !filterValue.updateAtRange || - (new Date(deployment.updatedAt) >= filterValue.updateAtRange!.from! && - new Date(deployment.updatedAt) <= filterValue.updateAtRange!.to!); + (new Date(Number(deployment.createdAt)) >= + filterValue.updateAtRange!.from! && + new Date(Number(deployment.createdAt)) <= + filterValue.updateAtRange!.to!); return branchMatch && statusMatch && dateMatch; }); From fc240c93d814cba707149f3a13cf197e61aaa373 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 21 Feb 2024 00:05:45 -0600 Subject: [PATCH 5/8] SO deployable config for the backend. (#83) * webapp-ify * SO deployable config for the backend. * Use process.env * Detangle --- packages/backend/src/constants.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index 7803a7b1..ea3b4610 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -1,8 +1,10 @@ -export const DEFAULT_CONFIG_FILE_PATH = 'environments/local.toml'; +import process from 'process'; + +export const DEFAULT_CONFIG_FILE_PATH = process.env.SNOWBALL_BACKEND_CONFIG_FILE_PATH || 'environments/local.toml'; export const DEFAULT_GQL_PATH = '/graphql'; // Note: temporary hardcoded user, later to be derived from auth token -export const USER_ID = '59f4355d-9549-4aac-9b54-eeefceeabef0'; +export const USER_ID = process.env.SNOWBALL_BACKEND_USER_ID || '60f4355d-9549-4aac-9b54-eeefceeabef0'; -export const PROJECT_DOMAIN = 'snowball.xyz'; +export const PROJECT_DOMAIN = process.env.SNOWBALL_BACKEND_PROJECT_DOMAIN || 'snowball.xyz'; From 8ca55cd88805f77b5341cac4356e74d25059bd08 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Wed, 21 Feb 2024 15:34:33 +0530 Subject: [PATCH 6/8] Implement polling for project deployment updates (#87) * Populate organization and user if db is empty * Use hardcoded user id from fixtures * Implement polling for get deployments query * Handle review changes --------- Co-authored-by: neeraj --- packages/backend/src/constants.ts | 2 +- packages/backend/src/database.ts | 32 +++++++++ packages/backend/src/utils.ts | 42 +++++++++++ packages/backend/test/initialize-db.ts | 70 ++++++------------- .../org-slug/projects/id/Deployments.tsx | 19 +++-- 5 files changed, 110 insertions(+), 55 deletions(-) diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index ea3b4610..bd3badad 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -5,6 +5,6 @@ export const DEFAULT_CONFIG_FILE_PATH = process.env.SNOWBALL_BACKEND_CONFIG_FILE export const DEFAULT_GQL_PATH = '/graphql'; // Note: temporary hardcoded user, later to be derived from auth token -export const USER_ID = process.env.SNOWBALL_BACKEND_USER_ID || '60f4355d-9549-4aac-9b54-eeefceeabef0'; +export const USER_ID = process.env.SNOWBALL_BACKEND_USER_ID || '59f4355d-9549-4aac-9b54-eeefceeabef0'; export const PROJECT_DOMAIN = process.env.SNOWBALL_BACKEND_PROJECT_DOMAIN || 'snowball.xyz'; diff --git a/packages/backend/src/database.ts b/packages/backend/src/database.ts index a45c354d..aa0e997f 100644 --- a/packages/backend/src/database.ts +++ b/packages/backend/src/database.ts @@ -14,6 +14,12 @@ import { ProjectMember } from './entity/ProjectMember'; import { EnvironmentVariable } from './entity/EnvironmentVariable'; import { Domain } from './entity/Domain'; import { PROJECT_DOMAIN } from './constants'; +import { getEntities, loadAndSaveData } from './utils'; +import { UserOrganization } from './entity/UserOrganization'; + +const ORGANIZATION_DATA_PATH = '../test/fixtures/organizations.json'; +const USER_DATA_PATH = '../test/fixtures/users.json'; +const USER_ORGANIZATION_DATA_PATH = '../test/fixtures/user-organizations.json'; const log = debug('snowball:database'); @@ -36,6 +42,25 @@ export class Database { async init (): Promise { await this.dataSource.initialize(); log('database initialized'); + + const organizations = await this.getOrganizations({}); + + if (!organizations.length) { + const orgEntities = await getEntities(path.resolve(__dirname, ORGANIZATION_DATA_PATH)); + const savedOrgs = await loadAndSaveData(Organization, this.dataSource, [orgEntities[0]]); + + // TODO: Remove user once authenticated + const userEntities = await getEntities(path.resolve(__dirname, USER_DATA_PATH)); + const savedUsers = await loadAndSaveData(User, this.dataSource, [userEntities[0]]); + + const userOrganizationRelations = { + member: savedUsers, + organization: savedOrgs + }; + + const userOrgEntities = await getEntities(path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH)); + await loadAndSaveData(UserOrganization, this.dataSource, [userOrgEntities[0]], userOrganizationRelations); + } } async getUser (options: FindOneOptions): Promise { @@ -60,6 +85,13 @@ export class Database { return updateResult.affected > 0; } + async getOrganizations (options: FindManyOptions): Promise { + const organizationRepository = this.dataSource.getRepository(Organization); + const organizations = await organizationRepository.find(options); + + return organizations; + } + async getOrganization (options: FindOneOptions): Promise { const organizationRepository = this.dataSource.getRepository(Organization); const organization = await organizationRepository.findOne(options); diff --git a/packages/backend/src/utils.ts b/packages/backend/src/utils.ts index f33963f9..f9d21e7a 100644 --- a/packages/backend/src/utils.ts +++ b/packages/backend/src/utils.ts @@ -2,6 +2,7 @@ import fs from 'fs-extra'; import path from 'path'; import toml from 'toml'; import debug from 'debug'; +import { DataSource, DeepPartial, EntityTarget, ObjectLiteral } from 'typeorm'; const log = debug('snowball:utils'); @@ -19,3 +20,44 @@ export const getConfig = async ( return config; }; + +export const checkFileExists = async (filePath: string): Promise => { + try { + await fs.access(filePath, fs.constants.F_OK); + return true; + } catch (err) { + log(err); + return false; + } +}; + +export const getEntities = async (filePath: string): Promise => { + const entitiesData = await fs.readFile(filePath, 'utf-8'); + const entities = JSON.parse(entitiesData); + return entities; +}; + +export const loadAndSaveData = async (entityType: EntityTarget, dataSource: DataSource, entities: any, relations?: any | undefined): Promise => { + const entityRepository = dataSource.getRepository(entityType); + + const savedEntity:Entity[] = []; + + for (const entityData of entities) { + let entity = entityRepository.create(entityData as DeepPartial); + + if (relations) { + for (const field in relations) { + const valueIndex = String(field + 'Index'); + + entity = { + ...entity, + [field]: relations[field][entityData[valueIndex]] + }; + } + } + const dbEntity = await entityRepository.save(entity); + savedEntity.push(dbEntity); + } + + return savedEntity; +}; diff --git a/packages/backend/test/initialize-db.ts b/packages/backend/test/initialize-db.ts index 0aade587..420ff03d 100644 --- a/packages/backend/test/initialize-db.ts +++ b/packages/backend/test/initialize-db.ts @@ -1,5 +1,4 @@ -import { DataSource, DeepPartial, EntityTarget, ObjectLiteral } from 'typeorm'; -import * as fs from 'fs/promises'; +import { DataSource } from 'typeorm'; import debug from 'debug'; import path from 'path'; @@ -11,7 +10,7 @@ import { EnvironmentVariable } from '../src/entity/EnvironmentVariable'; import { Domain } from '../src/entity/Domain'; import { ProjectMember } from '../src/entity/ProjectMember'; import { Deployment } from '../src/entity/Deployment'; -import { getConfig } from '../src/utils'; +import { checkFileExists, getConfig, getEntities, loadAndSaveData } from '../src/utils'; import { Config } from '../src/config'; import { DEFAULT_CONFIG_FILE_PATH } from '../src/constants'; @@ -27,43 +26,20 @@ const DEPLOYMENT_DATA_PATH = './fixtures/deployments.json'; const ENVIRONMENT_VARIABLE_DATA_PATH = './fixtures/environment-variables.json'; const REDIRECTED_DOMAIN_DATA_PATH = './fixtures/redirected-domains.json'; -const loadAndSaveData = async (entityType: EntityTarget, dataSource: DataSource, filePath: string, relations?: any | undefined) => { - const entitiesData = await fs.readFile(filePath, 'utf-8'); - const entities = JSON.parse(entitiesData); - const entityRepository = dataSource.getRepository(entityType); - - const savedEntity:Entity[] = []; - - for (const entityData of entities) { - let entity = entityRepository.create(entityData as DeepPartial); - - if (relations) { - for (const field in relations) { - const valueIndex = String(field + 'Index'); - - entity = { - ...entity, - [field]: relations[field][entityData[valueIndex]] - }; - } - } - const dbEntity = await entityRepository.save(entity); - savedEntity.push(dbEntity); - } - - return savedEntity; -}; - const generateTestData = async (dataSource: DataSource) => { - const savedUsers = await loadAndSaveData(User, dataSource, path.resolve(__dirname, USER_DATA_PATH)); - const savedOrgs = await loadAndSaveData(Organization, dataSource, path.resolve(__dirname, ORGANIZATION_DATA_PATH)); + const userEntities = await getEntities(path.resolve(__dirname, USER_DATA_PATH)); + const savedUsers = await loadAndSaveData(User, dataSource, userEntities); + + const orgEntities = await getEntities(path.resolve(__dirname, ORGANIZATION_DATA_PATH)); + const savedOrgs = await loadAndSaveData(Organization, dataSource, orgEntities); const projectRelations = { owner: savedUsers, organization: savedOrgs }; - const savedProjects = await loadAndSaveData(Project, dataSource, path.resolve(__dirname, PROJECT_DATA_PATH), projectRelations); + const projectEntities = await getEntities(path.resolve(__dirname, PROJECT_DATA_PATH)); + const savedProjects = await loadAndSaveData(Project, dataSource, projectEntities, projectRelations); const domainRepository = dataSource.getRepository(Domain); @@ -71,14 +47,16 @@ const generateTestData = async (dataSource: DataSource) => { project: savedProjects }; - const savedPrimaryDomains = await loadAndSaveData(Domain, dataSource, path.resolve(__dirname, PRIMARY_DOMAIN_DATA_PATH), domainPrimaryRelations); + const primaryDomainsEntities = await getEntities(path.resolve(__dirname, PRIMARY_DOMAIN_DATA_PATH)); + const savedPrimaryDomains = await loadAndSaveData(Domain, dataSource, primaryDomainsEntities, domainPrimaryRelations); const domainRedirectedRelations = { project: savedProjects, redirectTo: savedPrimaryDomains }; - await loadAndSaveData(Domain, dataSource, path.resolve(__dirname, REDIRECTED_DOMAIN_DATA_PATH), domainRedirectedRelations); + const redirectDomainsEntities = await getEntities(path.resolve(__dirname, REDIRECTED_DOMAIN_DATA_PATH)); + await loadAndSaveData(Domain, dataSource, redirectDomainsEntities, domainRedirectedRelations); const savedDomains = await domainRepository.find(); @@ -87,14 +65,16 @@ const generateTestData = async (dataSource: DataSource) => { organization: savedOrgs }; - await loadAndSaveData(UserOrganization, dataSource, path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH), userOrganizationRelations); + const userOrganizationsEntities = await getEntities(path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH)); + await loadAndSaveData(UserOrganization, dataSource, userOrganizationsEntities, userOrganizationRelations); const projectMemberRelations = { member: savedUsers, project: savedProjects }; - await loadAndSaveData(ProjectMember, dataSource, path.resolve(__dirname, PROJECT_MEMBER_DATA_PATH), projectMemberRelations); + const projectMembersEntities = await getEntities(path.resolve(__dirname, PROJECT_MEMBER_DATA_PATH)); + await loadAndSaveData(ProjectMember, dataSource, projectMembersEntities, projectMemberRelations); const deploymentRelations = { project: savedProjects, @@ -102,23 +82,15 @@ const generateTestData = async (dataSource: DataSource) => { createdBy: savedUsers }; - await loadAndSaveData(Deployment, dataSource, path.resolve(__dirname, DEPLOYMENT_DATA_PATH), deploymentRelations); + const deploymentsEntities = await getEntities(path.resolve(__dirname, DEPLOYMENT_DATA_PATH)); + await loadAndSaveData(Deployment, dataSource, deploymentsEntities, deploymentRelations); const environmentVariableRelations = { project: savedProjects }; - await loadAndSaveData(EnvironmentVariable, dataSource, path.resolve(__dirname, ENVIRONMENT_VARIABLE_DATA_PATH), environmentVariableRelations); -}; - -const checkFileExists = async (filePath: string) => { - try { - await fs.access(filePath, fs.constants.F_OK); - return true; - } catch (err) { - log(err); - return false; - } + const environmentVariablesEntities = await getEntities(path.resolve(__dirname, ENVIRONMENT_VARIABLE_DATA_PATH)); + await loadAndSaveData(EnvironmentVariable, dataSource, environmentVariablesEntities, environmentVariableRelations); }; const main = async () => { diff --git a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx index 958382ac..7049a270 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx @@ -16,6 +16,7 @@ const DEFAULT_FILTER_VALUE: FilterValue = { searchedBranch: '', status: StatusOptions.ALL_STATUS, }; +const FETCH_DEPLOYMENTS_INTERVAL = 5000; const DeploymentsTabPanel = () => { const client = useGQLClient(); @@ -26,22 +27,30 @@ const DeploymentsTabPanel = () => { const [deployments, setDeployments] = useState([]); const [prodBranchDomains, setProdBranchDomains] = useState([]); - const fetchDeployments = async () => { + const fetchDeployments = useCallback(async () => { const { deployments } = await client.getDeployments(project.id); setDeployments(deployments); - }; + }, [client]); const fetchProductionBranchDomains = useCallback(async () => { const { domains } = await client.getDomains(project.id, { branch: project.prodBranch, }); setProdBranchDomains(domains); - }, []); + }, [client]); useEffect(() => { - fetchDeployments(); fetchProductionBranchDomains(); - }, []); + fetchDeployments(); + + const interval = setInterval(() => { + fetchDeployments(); + }, FETCH_DEPLOYMENTS_INTERVAL); + + return () => { + clearInterval(interval); + }; + }, [fetchDeployments, fetchProductionBranchDomains]); const currentDeployment = useMemo(() => { return deployments.find((deployment) => { From 4d0f2ca893e7837ea48860ffc9ed23d6009815cf Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Wed, 21 Feb 2024 13:53:05 -0600 Subject: [PATCH 7/8] SO webapp-ify the frontend (#82) * webapp-ify * Tweak values * Self-contained --- build-webapp.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 build-webapp.sh diff --git a/build-webapp.sh b/build-webapp.sh new file mode 100755 index 00000000..e3e8db99 --- /dev/null +++ b/build-webapp.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +PKG_DIR="./packages/frontend" +OUTPUT_DIR="${PKG_DIR}/build" +DEST_DIR=${1:-/data} + +if [[ -d "$DEST_DIR" ]]; then + echo "${DEST_DIR} already exists." 1>&2 + exit 1 +fi + +cat > $PKG_DIR/.env <&2 + exit 1 +fi + +mv "$OUTPUT_DIR" "$DEST_DIR" From e816c596ca81ae7b5cab3bb65adda651bd81cb06 Mon Sep 17 00:00:00 2001 From: Nabarun Gogoi Date: Thu, 22 Feb 2024 10:04:33 +0530 Subject: [PATCH 8/8] Publish `ApplicationRecord` and `ApplicationDeploymentRequest` on creating new deployments (#89) * Create application deployment request on new deployment * Save application deployment request datas in deployment entity * Add project as dependency * Use project repo name as record app name --------- Co-authored-by: neeraj --- packages/backend/src/entity/Deployment.ts | 15 ++++++ packages/backend/src/entity/Project.ts | 15 ------ packages/backend/src/registry.ts | 25 +++++----- packages/backend/src/service.ts | 49 ++++++++++--------- packages/backend/src/types.ts | 4 +- .../backend/test/fixtures/deployments.json | 22 +++++++++ packages/backend/test/fixtures/projects.json | 10 ---- .../backend/test/publish-deploy-records.ts | 2 +- .../org-slug/projects/id/Deployments.tsx | 4 +- 9 files changed, 78 insertions(+), 68 deletions(-) diff --git a/packages/backend/src/entity/Deployment.ts b/packages/backend/src/entity/Deployment.ts index 62d3f38f..5771b5d2 100644 --- a/packages/backend/src/entity/Deployment.ts +++ b/packages/backend/src/entity/Deployment.ts @@ -26,6 +26,15 @@ export enum DeploymentStatus { Error = 'Error', } +export interface ApplicationDeploymentRequest { + type: string + version: string + name: string + application: string + config: string, + meta: string +} + export interface ApplicationRecord { type: string; version:string @@ -78,6 +87,12 @@ export class Deployment { @Column('simple-json') applicationRecordData!: ApplicationRecord; + @Column('varchar') + applicationDeploymentRequestId!: string; + + @Column('simple-json') + applicationDeploymentRequestData!: ApplicationDeploymentRequest; + @Column('varchar', { nullable: true }) applicationDeploymentRecordId!: string | null; diff --git a/packages/backend/src/entity/Project.ts b/packages/backend/src/entity/Project.ts index 86fa7b3d..4415476d 100644 --- a/packages/backend/src/entity/Project.ts +++ b/packages/backend/src/entity/Project.ts @@ -15,15 +15,6 @@ import { Organization } from './Organization'; import { ProjectMember } from './ProjectMember'; import { Deployment } from './Deployment'; -export interface ApplicationDeploymentRequest { - type: string - version: string - name: string - application: string - config: string, - meta: string -} - @Entity() export class Project { @PrimaryGeneratedColumn('uuid') @@ -52,12 +43,6 @@ export class Project { @Column('varchar', { length: 255, default: 'main' }) prodBranch!: string; - @Column('varchar', { nullable: true }) - applicationDeploymentRequestId!: string | null; - - @Column('simple-json', { nullable: true }) - applicationDeploymentRequestData!: ApplicationDeploymentRequest | null; - @Column('text', { default: '' }) description!: string; diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 108318bb..e3bce569 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -6,8 +6,7 @@ import { DateTime } from 'luxon'; import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk'; import { RegistryConfig } from './config'; -import { ApplicationDeploymentRequest } from './entity/Project'; -import { ApplicationRecord, Deployment } from './entity/Deployment'; +import { ApplicationRecord, Deployment, ApplicationDeploymentRequest } from './entity/Deployment'; import { AppDeploymentRecord, PackageJSON } from './types'; const log = debug('snowball:registry'); @@ -27,17 +26,18 @@ export class Registry { } async createApplicationRecord ({ + appName, packageJSON, commitHash, appType, repoUrl }: { + appName: string, packageJSON: PackageJSON commitHash: string, appType: string, repoUrl: string }): Promise<{applicationRecordId: string, applicationRecordData: ApplicationRecord}> { - assert(packageJSON.name, "name field doesn't exist in package.json"); // Use laconic-sdk to publish record // Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts/publish-app-record.sh // Fetch previous records @@ -60,7 +60,7 @@ export class Registry { repository_ref: commitHash, repository: [repoUrl], app_type: appType, - name: packageJSON.name, + name: appName, ...(packageJSON.description && { description: packageJSON.description }), ...(packageJSON.homepage && { homepage: packageJSON.homepage }), ...(packageJSON.license && { license: packageJSON.license }), @@ -81,7 +81,7 @@ export class Registry { log('Application record data:', applicationRecord); // TODO: Discuss computation of CRN - const crn = this.getCrn(packageJSON.name); + const crn = this.getCrn(packageJSON.name, appName); log(`Setting name: ${crn} for record ID: ${result.data.id}`); await this.registry.setName({ cid: result.data.id, crn }, this.registryConfig.privateKey, this.registryConfig.fee); @@ -93,6 +93,7 @@ export class Registry { async createApplicationDeploymentRequest (data: { appName: string, + packageJsonName: string, commitHash: string, repository: string, environmentVariables: { [key: string]: string } @@ -100,7 +101,7 @@ export class Registry { applicationDeploymentRequestId: string, applicationDeploymentRequestData: ApplicationDeploymentRequest }> { - const crn = this.getCrn(data.appName); + const crn = this.getCrn(data.packageJsonName, data.appName); const records = await this.registry.resolveNames([crn]); const applicationRecord = records[0]; @@ -159,14 +160,10 @@ export class Registry { return records.filter((record: AppDeploymentRecord) => deployments.some(deployment => deployment.applicationRecordId === record.attributes.application)); } - getCrn (packageJsonName: string): string { - const [arg1, arg2] = packageJsonName.split('/'); + getCrn (packageJsonName: string, appName: string): string { + const [arg1] = packageJsonName.split('/'); + const authority = arg1.replace('@', ''); - if (arg2) { - const authority = arg1.replace('@', ''); - return `crn://${authority}/applications/${arg2}`; - } - - return `crn://${arg1}/applications/${arg1}`; + return `crn://${authority}/applications/${appName}`; } } diff --git a/packages/backend/src/service.ts b/packages/backend/src/service.ts index 0ce326ff..401e9a18 100644 --- a/packages/backend/src/service.ts +++ b/packages/backend/src/service.ts @@ -15,7 +15,7 @@ import { Permission, ProjectMember } from './entity/ProjectMember'; import { User } from './entity/User'; import { Registry } from './registry'; import { GitHubConfig, RegistryConfig } from './config'; -import { AppDeploymentRecord, GitPushEventPayload } from './types'; +import { AppDeploymentRecord, GitPushEventPayload, PackageJSON } from './types'; const log = debug('snowball:service'); @@ -326,7 +326,9 @@ export class Service { } assert(!Array.isArray(packageJSONData) && packageJSONData.type === 'file'); - const packageJSON = JSON.parse(atob(packageJSONData.content)); + const packageJSON: PackageJSON = JSON.parse(atob(packageJSONData.content)); + + assert(packageJSON.name, "name field doesn't exist in package.json"); if (!recordData.repoUrl) { const { data: repoDetails } = await octokit.rest.repos.get({ owner, repo }); @@ -335,12 +337,30 @@ export class Service { // TODO: Set environment variables for each deployment (environment variables can`t be set in application record) const { applicationRecordId, applicationRecordData } = await this.registry.createApplicationRecord({ + appName: repo, packageJSON, appType: data.project!.template!, commitHash: data.commitHash!, repoUrl: recordData.repoUrl }); + const environmentVariables = await this.db.getEnvironmentVariablesByProjectId(data.project.id!, { environment: Environment.Production }); + + const environmentVariablesObj = environmentVariables.reduce((acc, env) => { + acc[env.key] = env.value; + + return acc; + }, {} as { [key: string]: string }); + + const { applicationDeploymentRequestId, applicationDeploymentRequestData } = await this.registry.createApplicationDeploymentRequest( + { + appName: repo, + packageJsonName: packageJSON.name, + commitHash: data.commitHash!, + repository: recordData.repoUrl, + environmentVariables: environmentVariablesObj + }); + // Update previous deployment with prod branch domain // TODO: Fix unique constraint error for domain await this.db.updateDeployment({ @@ -358,6 +378,8 @@ export class Service { status: DeploymentStatus.Building, applicationRecordId, applicationRecordData, + applicationDeploymentRequestId, + applicationDeploymentRequestData, domain: data.domain, createdBy: Object.assign(new User(), { id: userId @@ -393,7 +415,7 @@ export class Service { const { data: repoDetails } = await octokit.rest.repos.get({ owner, repo }); // Create deployment with prod branch and latest commit - const newDeployment = await this.createDeployment(userId, + await this.createDeployment(userId, octokit, { project, @@ -408,27 +430,6 @@ export class Service { } ); - const environmentVariables = await this.db.getEnvironmentVariablesByProjectId(project.id, { environment: Environment.Production }); - - const environmentVariablesObj = environmentVariables.reduce((acc, env) => { - acc[env.key] = env.value; - - return acc; - }, {} as { [key: string]: string }); - - const { applicationDeploymentRequestId, applicationDeploymentRequestData } = await this.registry.createApplicationDeploymentRequest( - { - appName: newDeployment.applicationRecordData.name!, - commitHash: latestCommit.sha, - repository: repoDetails.html_url, - environmentVariables: environmentVariablesObj - }); - - await this.db.updateProjectById(project.id, { - applicationDeploymentRequestId, - applicationDeploymentRequestData - }); - await this.createRepoHook(octokit, project); return project; diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 71626645..e0cef120 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,6 +1,6 @@ export interface PackageJSON { - name?: string; - version?: string; + name: string; + version: string; author?: string; description?: string; homepage?: string; diff --git a/packages/backend/test/fixtures/deployments.json b/packages/backend/test/fixtures/deployments.json index 5eb682a8..c0536072 100644 --- a/packages/backend/test/fixtures/deployments.json +++ b/packages/backend/test/fixtures/deployments.json @@ -9,6 +9,8 @@ "isCurrent": true, "applicationRecordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "xqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -24,6 +26,8 @@ "isCurrent": false, "applicationRecordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "wqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -39,6 +43,8 @@ "isCurrent": false, "applicationRecordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "kqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -54,6 +60,8 @@ "isCurrent": false, "applicationRecordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "yqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "prod", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -69,6 +77,8 @@ "isCurrent": true, "applicationRecordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "pqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -84,6 +94,8 @@ "isCurrent": false, "applicationRecordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "tqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -99,6 +111,8 @@ "isCurrent": false, "applicationRecordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "eqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -114,6 +128,8 @@ "isCurrent": true, "applicationRecordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "dqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "main", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -129,6 +145,8 @@ "isCurrent": false, "applicationRecordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", "applicationRecordData": {}, + "applicationDeploymentRequestId": "aqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -144,6 +162,8 @@ "isCurrent": false, "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", "applicationRecordData": {}, + "applicationDeploymentRequestId": "uqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", @@ -159,6 +179,8 @@ "isCurrent": true, "applicationRecordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo", "applicationRecordData": {}, + "applicationDeploymentRequestId": "pqbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", + "applicationDeploymentRequestData": {}, "branch": "test", "commitHash": "d5dfd7b827226b0d09d897346d291c256e113e00", "commitMessage": "subscription added", diff --git a/packages/backend/test/fixtures/projects.json b/packages/backend/test/fixtures/projects.json index 55c5ee23..f4564643 100644 --- a/packages/backend/test/fixtures/projects.json +++ b/packages/backend/test/fixtures/projects.json @@ -10,8 +10,6 @@ "framework": "test", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "testProject.snowball.xyz" }, { @@ -25,8 +23,6 @@ "framework": "test-2", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "testProject-2.snowball.xyz" }, { @@ -40,8 +36,6 @@ "framework": "test-3", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "iglootools.snowball.xyz" }, { @@ -55,8 +49,6 @@ "framework": "test-4", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "iglootools-2.snowball.xyz" }, { @@ -70,8 +62,6 @@ "framework": "test-5", "webhooks": [], "icon": "", - "applicationDeploymentRequestId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi", - "applicationDeploymentRequestData": {}, "subDomain": "snowball-2.snowball.xyz" } ] diff --git a/packages/backend/test/publish-deploy-records.ts b/packages/backend/test/publish-deploy-records.ts index f5636c4e..90ccc16e 100644 --- a/packages/backend/test/publish-deploy-records.ts +++ b/packages/backend/test/publish-deploy-records.ts @@ -53,7 +53,7 @@ async function main () { so: '66fcfa49a1664d4cb4ce4f72c1c0e151' }), - request: deployment.project.applicationDeploymentRequestId, + request: deployment.applicationDeploymentRequestId, url }; diff --git a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx index 7049a270..1d9e4e2d 100644 --- a/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx +++ b/packages/frontend/src/pages/org-slug/projects/id/Deployments.tsx @@ -30,14 +30,14 @@ const DeploymentsTabPanel = () => { const fetchDeployments = useCallback(async () => { const { deployments } = await client.getDeployments(project.id); setDeployments(deployments); - }, [client]); + }, [client, project]); const fetchProductionBranchDomains = useCallback(async () => { const { domains } = await client.getDomains(project.id, { branch: project.prodBranch, }); setProdBranchDomains(domains); - }, [client]); + }, [client, project]); useEffect(() => { fetchProductionBranchDomains();