Add record data to project and deployment entity

This commit is contained in:
neeraj 2024-02-09 15:18:35 +05:30
parent 2bce67ce87
commit f7643d6638
12 changed files with 121 additions and 57 deletions

View File

@ -25,6 +25,20 @@ export enum DeploymentStatus {
Error = 'Error',
}
export interface ApplicationRecord {
type: string;
version:string
name: string
description: string
homepage: string
license: string
author: string
repository: string,
app_version: string
repository_ref: string
app_type: string
}
@Entity()
export class Deployment {
// TODO: set custom generated id
@ -51,6 +65,9 @@ export class Deployment {
@Column('varchar')
recordId!: string;
@Column('simple-json')
recordData!: ApplicationRecord;
@Column({
enum: Environment
})

View File

@ -15,6 +15,17 @@ import { Organization } from './Organization';
import { ProjectMember } from './ProjectMember';
import { Deployment } from './Deployment';
export interface ApplicationDeploymentRequest {
type: string
version: string
name: string
application: string
config: {
env: {[key:string]: string}
},
meta: {[key:string]: string}
}
@Entity()
export class Project {
@PrimaryGeneratedColumn('uuid')
@ -43,6 +54,9 @@ export class Project {
@Column('varchar')
recordId!: string;
@Column('simple-json')
recordData!: ApplicationDeploymentRequest;
@Column('text', { default: '' })
description!: string;

View File

@ -29,7 +29,7 @@ export const main = async (): Promise<void> => {
const db = new Database(database);
await db.init();
const registry = new Registry({ registryConfig });
const registry = new Registry(registryConfig);
const service = new Service(db, app, registry);
const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString();

View File

@ -4,6 +4,8 @@ import { inc as semverInc } from 'semver';
import { Registry as LaconicRegistry } from '@cerc-io/laconic-sdk';
import { RegistryConfig } from './config';
import { ApplicationDeploymentRequest } from './entity/Project';
import { ApplicationRecord } from './entity/Deployment';
const log = debug('snowball:registry');
@ -15,13 +17,12 @@ export class Registry {
private registry: LaconicRegistry;
private registryConfig: RegistryConfig;
constructor ({ registryConfig }: { registryConfig: RegistryConfig }) {
constructor (registryConfig : RegistryConfig) {
this.registryConfig = registryConfig;
this.registry = new LaconicRegistry(registryConfig.gqlEndpoint, registryConfig.restEndpoint, registryConfig.chainId);
}
// TODO: Create record called from create deployment
async createApplicationRecord (data: { recordName: string }): Promise<string> {
async createApplicationRecord (data: { recordName: string, appType: string }): Promise<{recordId: string, recordData: ApplicationRecord}> {
// TODO: Get record name from repo package.json name
const recordName = data.recordName;
@ -41,7 +42,7 @@ export class Registry {
// Create record of type ApplicationRecord and publish
const applicationRecord = {
type: APP_RECORD_TYPE,
version: nextVersion,
version: nextVersion ?? '',
name: recordName,
// TODO: Get data from repo package.json
@ -54,9 +55,7 @@ export class Registry {
// TODO: Get latest commit hash from repo production branch / deployment
repository_ref: '10ac6678e8372a05ad5bb1c34c34',
// TODO: Get from project framework/template type
app_type: 'webapp'
app_type: data.appType
};
const result = await this.registry.setRecord(
@ -69,22 +68,20 @@ export class Registry {
this.registryConfig.fee
);
// TODO: Log deployment id
log(`Application record published for deployment: ${result.data.id}`);
log('Application record data:', applicationRecord);
// TODO: Discuss computation of crn
const crn = this.getCrn(AUTHORITY_NAME, data.recordName);
const crn = this.getCrn(data.recordName);
await this.registry.setName({ cid: result.data.id, crn }, this.registryConfig.privateKey, this.registryConfig.fee);
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 result.data.id;
return { recordId: result.data.id, recordData: applicationRecord };
}
async createApplicationDeploymentRequest (data: { appName: string }): Promise<string> {
const crn = this.getCrn(AUTHORITY_NAME, data.appName);
async createApplicationDeploymentRequest (data: { appName: string }): Promise<{recordId: string, recordData: ApplicationDeploymentRequest}> {
const crn = this.getCrn(data.appName);
const records = await this.registry.resolveNames([crn]);
const applicationRecord = records[0];
@ -127,10 +124,10 @@ export class Registry {
log(`Application deployment request record published: ${result.data.id}`);
log('Application deployment request data:', applicationDeploymentRequest);
return result.data.id;
return { recordId: result.data.id, recordData: applicationDeploymentRequest };
}
getCrn (authority: string, appName: string): string {
return `crn://${authority}/applications/${appName}`;
getCrn (appName: string): string {
return `crn://${AUTHORITY_NAME}/applications/${appName}`;
}
}

View File

@ -147,7 +147,7 @@ export const createResolvers = async (service: Service): Promise<any> => {
redeployToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => {
try {
return await service.redeployToProd(context.userId, deploymentId);
return Boolean(await service.redeployToProd(context.userId, deploymentId));
} catch (err) {
log(err);
return false;

View File

@ -132,6 +132,7 @@ input AddProjectInput {
name: String!
repository: String!
prodBranch: String!
template: String!
}
input UpdateProjectInput {

View File

@ -12,6 +12,9 @@ import { Project } from './entity/Project';
import { Permission, ProjectMember } from './entity/ProjectMember';
import { User } from './entity/User';
import { Registry } from './registry';
import debug from 'debug';
const log = debug('snowball:service');
export class Service {
private db: Database;
@ -166,7 +169,7 @@ export class Service {
const prodBranchDomains = await this.db.getDomainsByProjectId(oldDeployment.project.id, { branch: oldDeployment.project.prodBranch });
// TODO: Fix unique constraint error for domain
const DeploymentWithProdBranchDomain = await this.db.getDeployment({
const deploymentWithProdBranchDomain = await this.db.getDeployment({
where: {
domain: {
id: prodBranchDomains[0].id
@ -174,8 +177,8 @@ export class Service {
}
});
if (DeploymentWithProdBranchDomain) {
await this.db.updateDeploymentById(DeploymentWithProdBranchDomain.id, {
if (deploymentWithProdBranchDomain) {
await this.db.updateDeploymentById(deploymentWithProdBranchDomain.id, {
domain: null,
isCurrent: false
});
@ -197,34 +200,41 @@ export class Service {
await this.db.updateDeploymentById(oldCurrentDeployment.id, { isCurrent: false, domain: null });
}
const newDeployement = await this.createDeployment({
project: oldDeployment.project,
userId,
isCurrent: true,
environment: Environment.Production,
domain: prodBranchDomains[0],
commitHash: oldDeployment.commitHash
});
const newDeployement = await this.createDeployment(userId,
{
project: oldDeployment.project,
isCurrent: true,
branch: oldDeployment.branch,
environment: Environment.Production,
domain: prodBranchDomains[0],
commitHash: oldDeployment.commitHash
});
return newDeployement;
}
async createDeployment (data: { project: Project, userId: string, isCurrent: boolean, environment: Environment, domain?: Domain | null, commitHash: string }): Promise<Deployment> {
const applicationRecordId = await this.registry.createApplicationRecord({ recordName: data.project.name });
async createDeployment (userId: string, data: DeepPartial<Deployment>): Promise<Deployment> {
const { recordId, recordData } = await this.registry.createApplicationRecord({
recordName: data.project?.name ?? '',
appType: data.project?.template ?? ''
});
const newDeployement = await this.db.addDeployement({
project: data.project,
branch: data.project.prodBranch,
branch: data.branch,
commitHash: data.commitHash,
environment: data.environment,
isCurrent: data.isCurrent,
status: DeploymentStatus.Building,
recordId: applicationRecordId,
recordId,
recordData,
domain: data.domain,
createdBy: Object.assign(new User(), {
id: data.userId
id: userId
})
});
log(`Application record ${recordId} published for deployment ${newDeployement.id}`);
return newDeployement;
}
@ -240,22 +250,26 @@ export class Service {
const project = await this.db.addProject(userId, organization.id, {
...data,
recordId: ''
recordId: '',
recordData: {}
});
// TODO: Get repository details from github
await this.createDeployment({
project,
userId,
isCurrent: true,
environment: Environment.Production,
// TODO: Set latest commit hash
commitHash: ""
});
await this.createDeployment(userId,
{
project,
isCurrent: true,
branch: project.prodBranch,
environment: Environment.Production,
// TODO: Set latest commit hash
commitHash: '',
domain: null
});
const applicationDeploymentRequestRecordId = await this.registry.createApplicationDeploymentRequest({ appName: project.name });
const { recordId, recordData } = await this.registry.createApplicationDeploymentRequest({ appName: project.name });
await this.db.updateProjectById(project.id, {
recordId: applicationDeploymentRequestRecordId
recordId,
recordData
});
return project;
@ -283,7 +297,7 @@ export class Service {
return this.db.deleteDomainById(domainId);
}
async redeployToProd (userId: string, deploymentId: string): Promise<Deployment| boolean> {
async redeployToProd (userId: string, deploymentId: string): Promise<Deployment> {
const oldDeployment = await this.db.getDeployment({
relations: {
project: true,
@ -299,19 +313,20 @@ export class Service {
throw new Error('Deployment not found');
}
const oldDeploymentUpdated = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false });
await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false });
const newDeployement = await this.createDeployment({
project: oldDeployment.project,
userId: userId,
// TODO: Put isCurrent field in project
isCurrent: true,
environment: Environment.Production,
domain: oldDeployment.domain,
commitHash: oldDeployment.commitHash
});
const newDeployement = await this.createDeployment(userId,
{
project: oldDeployment.project,
// TODO: Put isCurrent field in project
branch: oldDeployment.branch,
isCurrent: true,
environment: Environment.Production,
domain: oldDeployment.domain,
commitHash: oldDeployment.commitHash
});
return oldDeploymentUpdated && Boolean(newDeployement);
return newDeployement;
}
async rollbackDeployment (projectId: string, deploymentId: string): Promise<boolean> {

View File

@ -8,6 +8,7 @@
"environment": "Production",
"isCurrent": true,
"recordId": "qbafyrehvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "main",
"commitHash": "testXyz",
"url": "testProject-ffhae3zq.snowball.xyz"
@ -21,6 +22,7 @@
"environment": "Preview",
"isCurrent": false,
"recordId": "wbafyreihvzya6ovp4yfpkqnddkui2iw7thbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "test",
"commitHash": "testXyz",
"url": "testProject-vehagei8.snowball.xyz"
@ -34,6 +36,7 @@
"environment": "Development",
"isCurrent": false,
"recordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "test",
"commitHash": "testXyz",
"url": "testProject-qmgekyte.snowball.xyz"
@ -47,6 +50,7 @@
"environment": "Production",
"isCurrent": false,
"recordId": "rbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhw74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "prod",
"commitHash": "testXyz",
"url": "testProject-f8wsyim6.snowball.xyz"
@ -60,6 +64,7 @@
"environment": "Production",
"isCurrent": true,
"recordId": "tbafyreihvzya6ovp4yfpqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "main",
"commitHash": "testXyz",
"url": "testProject-2-eO8cckxk.snowball.xyz"
@ -73,6 +78,7 @@
"environment": "Preview",
"isCurrent": false,
"recordId": "ybafyreihvzya6ovp4yfpkqnddkui2iw7t6bhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "test",
"commitHash": "testXyz",
"url": "testProject-2-yaq0t5yw.snowball.xyz"
@ -86,6 +92,7 @@
"environment": "Development",
"isCurrent": false,
"recordId": "ubafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvfhrowoi",
"recordData": {},
"branch": "test",
"commitHash": "testXyz",
"url": "testProject-2-hwwr6sbx.snowball.xyz"
@ -99,6 +106,7 @@
"environment": "Production",
"isCurrent": true,
"recordId": "ibayreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "main",
"commitHash": "testXyz",
"url": "iglootools-ndxje48a.snowball.xyz"
@ -112,6 +120,7 @@
"environment": "Preview",
"isCurrent": false,
"recordId": "obafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"branch": "test",
"commitHash": "testXyz",
"url": "iglootools-gtgpgvei.snowball.xyz"
@ -125,6 +134,7 @@
"environment": "Development",
"isCurrent": false,
"recordId": "pbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowo",
"recordData": {},
"branch": "test",
"commitHash": "testXyz",
"url": "iglootools-b4bpthjr.snowball.xyz"

View File

@ -11,6 +11,7 @@
"webhooks": [],
"icon": "",
"recordId": "hbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"subDomain": "testProject.snowball.xyz"
},
{
@ -25,6 +26,7 @@
"webhooks": [],
"icon": "",
"recordId": "gbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"subDomain": "testProject-2.snowball.xyz"
},
{
@ -39,6 +41,7 @@
"webhooks": [],
"icon": "",
"recordId": "ebafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"subDomain": "iglootools.snowball.xyz"
},
{
@ -53,6 +56,7 @@
"webhooks": [],
"icon": "",
"recordId": "qbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"subDomain": "iglootools-2.snowball.xyz"
},
{
@ -67,6 +71,7 @@
"webhooks": [],
"icon": "",
"recordId": "xbafyreihvzya6ovp4yfpkqnddkui2iw7t6hbhwq74lbqs7bhobvmfhrowoi",
"recordData": {},
"subDomain": "snowball-2.snowball.xyz"
}
]

View File

@ -26,6 +26,8 @@ const ProjectRepoCard: React.FC<ProjectRepoCardProps> = ({ repository }) => {
name: `${repository.owner!.login}-${repository.name}`,
prodBranch: repository.default_branch!,
repository: repository.full_name,
// TODO: Compute template from repo
template: 'webapp',
});
navigate(`import?projectId=${addProject.id}`);

View File

@ -52,6 +52,8 @@ const CreateRepo = () => {
name: `${gitRepo.data.owner!.login}-${gitRepo.data.name}`,
prodBranch: gitRepo.data.default_branch ?? 'main',
repository: gitRepo.data.full_name,
// TODO: Set selected template
template: 'webapp',
});
navigate(

View File

@ -243,6 +243,7 @@ export type AddProjectInput = {
name: string;
repository: string;
prodBranch: string;
template: string;
}
export type UpdateProjectInput = {