Skip to content

Commit

Permalink
Delete Product from BuildEngine
Browse files Browse the repository at this point in the history
  • Loading branch information
FyreByrd committed Oct 10, 2024
1 parent e8e6651 commit df44fda
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 34 deletions.
7 changes: 7 additions & 0 deletions source/SIL.AppBuilder.Portal/common/BullJobTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export enum ScriptoriaJobType {
Notify_Reviewers = 'Notify Reviewers',
// Product Tasks
Product_Create = 'Create Product',
Product_Delete = 'Delete Product',
// Project Tasks
Project_Create = 'Create Project',
Project_Check = 'Check Project Creation',
Expand Down Expand Up @@ -52,6 +53,11 @@ export namespace Product {
type: ScriptoriaJobType.Product_Create;
productId: string;
}
export interface Delete {
type: ScriptoriaJobType.Product_Delete;
organizationId: number;
workflowJobId: number;
}
}

export namespace Project {
Expand Down Expand Up @@ -147,6 +153,7 @@ export type JobTypeMap = {
[ScriptoriaJobType.Build_Check]: Build.Check;
[ScriptoriaJobType.Notify_Reviewers]: Notify.Reviewers;
[ScriptoriaJobType.Product_Create]: Product.Create;
[ScriptoriaJobType.Product_Delete]: Product.Delete;
[ScriptoriaJobType.Project_Create]: Project.Create;
[ScriptoriaJobType.Project_Check]: Project.Check;
[ScriptoriaJobType.Publish_Product]: Publish.Product;
Expand Down
32 changes: 23 additions & 9 deletions source/SIL.AppBuilder.Portal/common/build-engine-api/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export async function request(
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
'Content-Type': body? 'application/json' : undefined
'Content-Type': body ? 'application/json' : undefined
},
body: body ? JSON.stringify(body) : undefined
});
Expand Down Expand Up @@ -71,9 +71,14 @@ export async function getProject(
? ((await res.json()) as Types.ProjectResponse)
: ((await res.json()) as Types.ErrorResponse);
}
export async function deleteProject(organizationId: number, projectId: number): Promise<number> {
export async function deleteProject(
organizationId: number,
projectId: number
): Promise<Types.DeleteResponse | Types.ErrorResponse> {
const res = await request(`project/${projectId}`, organizationId, 'DELETE');
return res.status;
return res.ok
? { responseType: 'delete', status: res.status }
: ((await res.json()) as Types.ErrorResponse);
}

export async function getProjectAccessToken(
Expand Down Expand Up @@ -113,9 +118,14 @@ export async function getJob(
? ((await res.json()) as Types.JobResponse)
: ((await res.json()) as Types.ErrorResponse);
}
export async function deleteJob(organizationId: number, jobId: number): Promise<number> {
export async function deleteJob(
organizationId: number,
jobId: number
): Promise<Types.DeleteResponse | Types.ErrorResponse> {
const res = await request(`job/${jobId}`, organizationId, 'DELETE');
return res.status;
return res.ok
? { responseType: 'delete', status: res.status }
: ((await res.json()) as Types.ErrorResponse);
}

export async function createBuild(
Expand Down Expand Up @@ -151,9 +161,11 @@ export async function deleteBuild(
organizationId: number,
jobId: number,
buildId: number
): Promise<number> {
): Promise<Types.DeleteResponse | Types.ErrorResponse> {
const res = await request(`job/${jobId}/build/${buildId}`, organizationId, 'DELETE');
return res.status;
return res.ok
? { responseType: 'delete', status: res.status }
: ((await res.json()) as Types.ErrorResponse);
}

export async function createRelease(
Expand Down Expand Up @@ -183,11 +195,13 @@ export async function deleteRelease(
jobId: number,
buildId: number,
releaseId: number
): Promise<number> {
): Promise<Types.DeleteResponse | Types.ErrorResponse> {
const res = await request(
`job/${jobId}/build/${buildId}/release/${releaseId}`,
organizationId,
'DELETE'
);
return res.status;
return res.ok
? { responseType: 'delete', status: res.status }
: ((await res.json()) as Types.ErrorResponse);
}
5 changes: 5 additions & 0 deletions source/SIL.AppBuilder.Portal/common/build-engine-api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export type ErrorResponse = {
type: string;
};

export type DeleteResponse = {
responseType: 'delete';
status: number;
};

type SuccessResponse = {
responseType: 'project' | 'token' | 'job' | 'build' | 'release';
id: number;
Expand Down
63 changes: 45 additions & 18 deletions source/SIL.AppBuilder.Portal/common/databaseProxy/Products.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Prisma } from '@prisma/client';
import prisma from '../prisma.js';
import { RequirePrimitive } from './utility.js';
import { BullMQ, queues } from '../index.js';

export async function create(
productData: RequirePrimitive<Prisma.ProductsUncheckedCreateInput>
Expand Down Expand Up @@ -43,12 +44,8 @@ export async function update(
const productDefinitionId = productData.ProductDefinitionId ?? existing!.ProductDefinitionId;
const storeId = productData.StoreId ?? existing!.StoreId;
const storeLanguageId = productData.StoreLanguageId ?? existing!.StoreLanguageId;
if (!(await validateProductBase(
projectId,
productDefinitionId,
storeId,
storeLanguageId
))) return false;
if (!(await validateProductBase(projectId, productDefinitionId, storeId, storeLanguageId)))
return false;

// No additional verification steps

Expand All @@ -66,8 +63,36 @@ export async function update(
return true;
}

function deleteProduct(productId: string) {
async function deleteProduct(productId: string) {
// Delete all userTasks for this product, and delete the product
const product = await prisma.products.findUnique({
where: {
Id: productId
},
select: {
Project: {
select: {
OrganizationId: true
}
},
WorkflowJobId: true
}
});
queues.scriptoria.add(
`Delete Product #${productId} from BuildEngine`,
{
type: BullMQ.ScriptoriaJobType.Product_Delete,
organizationId: product.Project.OrganizationId,
workflowJobId: product.WorkflowJobId
},
{
attempts: 5,
backoff: {
type: 'exponential',
delay: 5000 // 5 seconds
}
}
);
return prisma.$transaction([
prisma.workflowInstances.delete({
where: {
Expand All @@ -85,7 +110,6 @@ function deleteProduct(productId: string) {
}
})
]);
// TODO: delete from BuildEngine
}
export { deleteProduct as delete };

Expand Down Expand Up @@ -140,15 +164,17 @@ async function validateProductBase(
// Store type must match Workflow store type
Id: true,
// StoreLanguage must be allowed by Store, if the StoreLanguage is defined
StoreLanguages: storeLanguageId === undefined || storeLanguageId === null ?
undefined : {
where: {
Id: storeLanguageId
},
select: {
Id: true
}
}
StoreLanguages:
storeLanguageId === undefined || storeLanguageId === null
? undefined
: {
where: {
Id: storeLanguageId
},
select: {
Id: true
}
}
}
}
}
Expand All @@ -175,7 +201,8 @@ async function validateProductBase(
// 2. The project has a WorkflowProjectUrl
// handled by query
// 4. The language is allowed by the store
(storeLanguageId ?? project.Organization.OrganizationStores[0].Store.StoreType.StoreLanguages.length > 0) &&
(storeLanguageId ??
project.Organization.OrganizationStores[0].Store.StoreType.StoreLanguages.length > 0) &&
// 5. The product type is allowed by the organization
project.Organization.OrganizationProductDefinitions.length > 0
);
Expand Down
2 changes: 2 additions & 0 deletions source/SIL.AppBuilder.Portal/node-server/BullWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export class ScriptoriaWorker extends BullWorker<BullMQ.ScriptoriaJob, number> {
return new Executor.Notify.Reviewers().execute(job as JobCast<BullMQ.Notify.Reviewers>);
case BullMQ.ScriptoriaJobType.Product_Create:
return new Executor.Product.Create().execute(job as JobCast<BullMQ.Product.Create>);
case BullMQ.ScriptoriaJobType.Product_Delete:
return new Executor.Product.Delete().execute(job as JobCast<BullMQ.Product.Delete>);
case BullMQ.ScriptoriaJobType.Project_Create:
return new Executor.Project.Create().execute(job as JobCast<BullMQ.Project.Create>);
case BullMQ.ScriptoriaJobType.Project_Check:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { Job } from 'bullmq';
import { ScriptoriaJobExecutor } from './base.js';

// TODO: What would be a meaningful return?
export class Product extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Build_Product> {
async execute(job: Job<BullMQ.Build.Product, number, string>): Promise<number> {
const productData = await prisma.products.findUnique({
Expand Down Expand Up @@ -37,7 +36,7 @@ export class Product extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Buil
job.updateProgress(50);
if (response.responseType === 'error') {
const flow = await Workflow.restore(job.data.productId);
// TODO: How best to notify of failure?
// TODO: Match DWKit failure output
flow.send({ type: 'Build Failed', userId: null, comment: response.message });
job.updateProgress(100);
return 0;
Expand Down
19 changes: 17 additions & 2 deletions source/SIL.AppBuilder.Portal/node-server/job-executors/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
import { Job } from 'bullmq';
import { ScriptoriaJobExecutor } from './base.js';

// TODO: What would be a meaningful return?
export class Create extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Product_Create> {
async execute(job: Job<BullMQ.Product.Create, number, string>): Promise<number> {
const productData = await prisma.products.findUnique({
Expand Down Expand Up @@ -43,7 +42,6 @@ export class Create extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Produ
});
job.updateProgress(50);
if (response.responseType === 'error') {
// TODO: What do I do here? Wait some period of time and retry?
job.log(response.message);
throw new Error(response.message);
} else {
Expand All @@ -60,3 +58,20 @@ export class Create extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Produ
}
}
}

export class Delete extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Product_Delete> {
async execute(job: Job<BullMQ.Product.Delete, number, string>): Promise<number> {
const response = await BuildEngine.Requests.deleteJob(
job.data.organizationId,
job.data.workflowJobId
);
job.updateProgress(50);
if (response.responseType === 'error') {
job.log(response.message);
throw new Error(response.message);
} else {
job.updateProgress(100);
return response.status;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
import { Job } from 'bullmq';
import { ScriptoriaJobExecutor } from './base.js';

// TODO: What would be a meaningful return?
export class Create extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Project_Create> {
async execute(job: Job<BullMQ.Project.Create, number, string>): Promise<number> {
const projectData = await prisma.projects.findUnique({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
import { Job } from 'bullmq';
import { ScriptoriaJobExecutor } from './base.js';

// TODO: What would be a meaningful return?
export class Product extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Publish_Product> {
async execute(job: Job<BullMQ.Publish.Product, number, string>): Promise<number> {
const productData = await prisma.products.findUnique({
Expand Down Expand Up @@ -40,7 +39,7 @@ export class Product extends ScriptoriaJobExecutor<BullMQ.ScriptoriaJobType.Publ
job.updateProgress(50);
if (response.responseType === 'error') {
const flow = await Workflow.restore(job.data.productId);
// TODO: How best to notify of failure?
// TODO: Match DWKit failure output
flow.send({ type: 'Publish Failed', userId: null, comment: response.message });
job.updateProgress(100);
return 0;
Expand Down

0 comments on commit df44fda

Please sign in to comment.