Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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: 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/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: 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
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