Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/lib/products/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { DatabaseReads, DatabaseWrites } from '$lib/server/database';
import { Workflow } from '$lib/server/workflow';
import { ProductActionType } from '.';

export async function doProductAction(productId: string, action: ProductActionType) {
export async function doProductAction(
productId: string,
action: ProductActionType,
isAutomatic = false
) {
const product = await DatabaseReads.products.findUnique({
where: {
Id: productId
Expand Down Expand Up @@ -49,7 +53,8 @@ export async function doProductAction(productId: string, action: ProductActionTy
await Workflow.create(productId, {
productType: product.ProductDefinition[flowType].ProductType,
options: new Set(product.ProductDefinition[flowType].WorkflowOptions),
workflowType: product.ProductDefinition[flowType].Type
workflowType: product.ProductDefinition[flowType].Type,
isAutomatic
});
}
break;
Expand Down
4 changes: 3 additions & 1 deletion src/lib/server/email-service/locales/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"releaseFailedOwner": "Scriptoria: Publish {{projectName}} {{productName}} Failed",
"releaseFailedAdmin": "Scriptoria: Publish {{projectName}} {{productName}} Failed",
"releaseProductRecordNotFound": "Scriptoria: Release Failed: Product {{productId}}",
"autoPublishOnRebuildCompleted": "Scriptoria: Auto publish {{projectName}} {{productName}} Successful",
"reviewProduct": "{{projectName}} app - ready for review",
"reviewProductWithComment": "{{projectName}} app - ready for review",
"reviewProductNoPlayListing": "{{projectName}} app - ready for review",
Expand Down Expand Up @@ -113,7 +114,8 @@
"releaseCompletedSuccessfully": "Release for product: {{productName}} project: {{projectName}} completed successfully",
"releaseFailedOwner": "Publish for product {{productName}} project {{projectName}} failed. The organization administrator has been notified of this issue.",
"releaseFailedAdmin": "Publish for product {{productName}} project {{projectName}} failed. Review logs in email for details.",
"releaseProductRecordNotFound": "Release Failed: Unable to find product record with id: {{productId}}"
"releaseProductRecordNotFound": "Release Failed: Unable to find product record with id: {{productId}}",
"autoPublishOnRebuildCompleted": "Automatic publish for product {{productName}} in project {{projectName}} completed successfully"
}
},
"organizationInvites": {
Expand Down
4 changes: 3 additions & 1 deletion src/lib/server/email-service/locales/es-419.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"releaseFailedOwner": "Scriptoria: Publish {{projectName}} {{productName}} Failed",
"releaseFailedAdmin": "Scriptoria: Publish {{projectName}} {{productName}} Failed",
"releaseProductRecordNotFound": "Scriptoria: Release Failed: Product {{productId}}",
"autoPublishOnRebuildCompleted": "Scriptoria: Publicación automática de {{productName}} en {{projectName}} completada con éxito",
"reviewProduct": "{{projectName}} app - ready for review",
"reviewProductWithComment": "{{projectName}} app - ready for review",
"reviewProductNoPlayListing": "{{projectName}} app - ready for review",
Expand Down Expand Up @@ -113,7 +114,8 @@
"releaseCompletedSuccessfully": "Release for product: {{productName}} project: {{projectName}} completed successfully",
"releaseFailedOwner": "Publish for product {{productName}} project {{projectName}} failed. The organization administrator has been notified of this issue.",
"releaseFailedAdmin": "Publish for product {{productName}} project {{projectName}} failed. Review logs in email for details.",
"releaseProductRecordNotFound": "Release Failed: Unable to find product record with id: {{productId}}"
"releaseProductRecordNotFound": "Release Failed: Unable to find product record with id: {{productId}}",
"autoPublishOnRebuildCompleted": "Publicación automática del producto {{productName}} en el proyecto {{projectName}} completada con éxito"
}
},
"organizationInvites": {
Expand Down
4 changes: 3 additions & 1 deletion src/lib/server/email-service/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"releaseFailedOwner": "Scriptoria: Publish {{projectName}} {{productName}} Failed",
"releaseFailedAdmin": "Scriptoria: Publish {{projectName}} {{productName}} Failed",
"releaseProductRecordNotFound": "Scriptoria: Release Failed: Product {{productId}}",
"autoPublishOnRebuildCompleted": "Scriptoria : Publication automatique réussie de {{productName}} dans {{projectName}}",
"reviewProduct": "{{projectName}} app - ready for review",
"reviewProductWithComment": "{{projectName}} app - ready for review",
"reviewProductNoPlayListing": "{{projectName}} app - ready for review",
Expand Down Expand Up @@ -113,7 +114,8 @@
"releaseCompletedSuccessfully": "Release for product: {{productName}} project: {{projectName}} completed successfully",
"releaseFailedOwner": "Publish for product {{productName}} project {{projectName}} failed. The organization administrator has been notified of this issue.",
"releaseFailedAdmin": "Publish for product {{productName}} project {{projectName}} failed. Review logs in email for details.",
"releaseProductRecordNotFound": "Release Failed: Unable to find product record with id: {{productId}}"
"releaseProductRecordNotFound": "Release Failed: Unable to find product record with id: {{productId}}",
"autoPublishOnRebuildCompleted": "La publication automatique du produit {{productName}} dans le projet {{projectName}} s'est terminée avec succès."
}
},
"organizationInvites": {
Expand Down
3 changes: 2 additions & 1 deletion src/lib/server/job-executors/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ export async function createLocal(job: Job<BullMQ.Product.CreateLocal>): Promise
await Workflow.create(productId, {
productType: flowDefinition.ProductType,
options: new Set(flowDefinition.WorkflowOptions),
workflowType: flowDefinition.Type
workflowType: flowDefinition.Type,
isAutomatic: false
});
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/server/job-executors/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,7 @@ async function tryCreateInstance(
includeFields: [],
includeArtifacts: null,
includeReviewers: false,
isAutomatic: false,
environment: mergedEnv,
start: ActivityName as WorkflowState
} satisfies WorkflowInstanceContext),
Expand Down
30 changes: 30 additions & 0 deletions src/lib/server/workflow/dbProcedures.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ProductTransitionType } from '../../prisma';
import { BullMQ, getQueues } from '../bullmq';
import { DatabaseWrites } from '../database';
import { DatabaseReads } from '../database/prisma';

Expand Down Expand Up @@ -50,3 +51,32 @@ export async function markResolved(productId: string) {
});
}
}

export async function notifyAutoPublishOwner(productId: string) {
const product = await DatabaseReads.products.findUnique({
where: { Id: productId },
select: {
ProductDefinition: {
select: {
Name: true
}
},
Project: {
select: {
Name: true,
OwnerId: true
}
}
}
});
if (!product?.Project.OwnerId) return;
await getQueues().Emails.add(`Notify Owner of Auto Publish for Product #${productId}`, {
type: BullMQ.JobType.Email_SendNotificationToUser,
userId: product.Project.OwnerId,
messageKey: 'autoPublishOnRebuildCompleted',
messageProperties: {
projectName: product.Project.Name ?? '',
productName: product.ProductDefinition.Name ?? ''
}
});
}
10 changes: 9 additions & 1 deletion src/lib/server/workflow/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class Workflow {
select: {
Project: {
select: {
AutoPublishOnRebuild: true,
_count: {
select: {
Authors: true,
Expand All @@ -70,6 +71,7 @@ export class Workflow {
...config,
hasAuthors: !!check?.Project._count.Authors,
hasReviewers: !!check?.Project._count.Reviewers,
autoPublishOnRebuild: !!check?.Project.AutoPublishOnRebuild,
productId
});
flow.flow = createActor(WorkflowStateMachine, {
Expand Down Expand Up @@ -166,6 +168,7 @@ export class Workflow {
select: {
Project: {
select: {
AutoPublishOnRebuild: true,
_count: {
select: {
Authors: true,
Expand All @@ -181,17 +184,21 @@ export class Workflow {
if (!instance) {
return null;
}
const context = JSON.parse(instance.Context) as WorkflowInstanceContext;
context.isAutomatic ??= false;
return {
instanceId: instance.Id,
definitionId: instance.WorkflowDefinition.Id,
state: instance.State,
context: JSON.parse(instance.Context) as WorkflowInstanceContext,
context,
input: {
workflowType: instance.WorkflowDefinition.Type,
productType: instance.WorkflowDefinition.ProductType,
options: new Set(instance.WorkflowDefinition.WorkflowOptions),
hasAuthors: !!instance.Product.Project._count.Authors,
hasReviewers: !!instance.Product.Project._count.Reviewers,
autoPublishOnRebuild: !!instance.Product.Project.AutoPublishOnRebuild,
isAutomatic: context.isAutomatic ?? false,
productId
}
};
Expand Down Expand Up @@ -362,6 +369,7 @@ export class Workflow {
productId: undefined,
hasAuthors: undefined,
hasReviewers: undefined,
autoPublishOnRebuild: undefined,
productType: undefined,
options: undefined
} as WorkflowInstanceContext;
Expand Down
30 changes: 28 additions & 2 deletions src/lib/server/workflow/state-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ import {
WorkflowAction,
WorkflowOptions,
WorkflowState,
autoPublishOnRebuild,
hasAuthors,
hasReviewers,
isAuthorState,
isDeprecated,
jump
} from '../../workflowTypes';
import { BullMQ, getQueues } from '../bullmq';
import { deleteWorkflow, markResolved } from './dbProcedures';
import { deleteWorkflow, markResolved, notifyAutoPublishOwner } from './dbProcedures';

/**
* IMPORTANT: READ THIS BEFORE EDITING A STATE MACHINE!
Expand Down Expand Up @@ -56,12 +57,14 @@ export const WorkflowStateMachine = setup({
/** Reset to null on exit */
includeArtifacts: null,
environment: {},
isAutomatic: input.isAutomatic,
workflowType: input.workflowType,
productType: input.productType,
options: input.options,
productId: input.productId,
hasAuthors: input.hasAuthors,
hasReviewers: input.hasReviewers
hasReviewers: input.hasReviewers,
autoPublishOnRebuild: input.autoPublishOnRebuild
}),
states: {
[WorkflowState.Start]: {
Expand Down Expand Up @@ -543,6 +546,16 @@ export const WorkflowStateMachine = setup({
!context.environment[ENVKeys.PUBLISH_GOOGLE_PLAY_UPLOADED_BUILD_ID],
target: WorkflowState.App_Store_Preview
},
{
meta: {
type: ActionType.Auto,
includeWhen: {
guards: [autoPublishOnRebuild]
}
},
guard: autoPublishOnRebuild,
target: WorkflowState.Product_Publish
},
{
// this is the normal transition for a successful build
meta: { type: ActionType.Auto },
Expand Down Expand Up @@ -808,6 +821,11 @@ export const WorkflowStateMachine = setup({
workflowType: { is: WorkflowType.Startup }
}
},
actions: ({ context }) => {
if (context.autoPublishOnRebuild && context.isAutomatic) {
void notifyAutoPublishOwner(context.productId);
}
},
guard: ({ context }) =>
context.productType === ProductType.Android_GooglePlay &&
!context.environment[ENVKeys.GOOGLE_PLAY_EXISTING] &&
Expand All @@ -816,6 +834,14 @@ export const WorkflowStateMachine = setup({
},
{
meta: { type: ActionType.Auto },
actions: ({ context }) => {
if (context.autoPublishOnRebuild && context.isAutomatic) {
void notifyAutoPublishOwner(context.productId);
}
},
guard: ({ context }) =>
context.productType !== ProductType.Android_GooglePlay ||
context.environment[ENVKeys.GOOGLE_PLAY_EXISTING] === '1',
target: WorkflowState.Published
}
],
Expand Down
15 changes: 13 additions & 2 deletions src/lib/workflowTypes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type TransitionConfig, and } from 'xstate';
import type { RoleId, WorkflowType } from './prisma';
import { WorkflowType } from './prisma';
import type { RoleId } from './prisma';
import type { SetFilter, ValueFilter } from './utils';
import { filterSet, filterValue } from './utils';

Expand Down Expand Up @@ -132,6 +133,7 @@ export type WorkflowInstanceContext = {
includeArtifacts: ArtifactLists | null;
start?: WorkflowState;
environment: Environment;
isAutomatic: boolean;
};

export type ArtifactLists = 'latestAAB' | 'latestAssetPackage' | 'error' | 'all';
Expand Down Expand Up @@ -181,12 +183,14 @@ export type WorkflowConfig = {
options: Set<WorkflowOptions>;
productType: ProductType;
workflowType: WorkflowType;
isAutomatic: boolean;
};

export type WorkflowInput = WorkflowConfig & {
productId: string;
hasAuthors: boolean;
hasReviewers: boolean;
autoPublishOnRebuild: boolean;
};

/** Used for filtering based on specified WorkflowOptions and/or ProductType */
Expand Down Expand Up @@ -258,7 +262,14 @@ export function hasAuthors(args: { context: WorkflowInput }): boolean {
export function hasReviewers(args: { context: WorkflowInput }): boolean {
return args.context.hasReviewers;
}
export type Guards = typeof hasAuthors | typeof hasReviewers;
export function autoPublishOnRebuild(args: { context: WorkflowInput }): boolean {
return (
args.context.autoPublishOnRebuild &&
args.context.isAutomatic &&
args.context.workflowType === WorkflowType.Rebuild
);
}
export type Guards = typeof hasAuthors | typeof hasReviewers | typeof autoPublishOnRebuild;
/**
* @param params expected params of `canJump` guard from StartupWorkflow
* @param optionalGuards other guards that can optionally be added.
Expand Down
Loading