Skip to content
Merged
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
2 changes: 1 addition & 1 deletion ghost/core/core/server/data/seeders/data-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const path = require('path');
const fs = require('fs/promises');
const JsonImporter = require('./utils/json-importer');
const {getProcessRoot} = require('@tryghost/root-utils');
const topologicalSort = require('./utils/topological-sort');
const {topologicalSort} = require('./utils/topological-sort');
const {faker} = require('@faker-js/faker');
const {faker: americanFaker} = require('@faker-js/faker/locale/en_US');
const crypto = require('crypto');
Expand Down
33 changes: 0 additions & 33 deletions ghost/core/core/server/data/seeders/utils/topological-sort.js

This file was deleted.

48 changes: 48 additions & 0 deletions ghost/core/core/server/data/seeders/utils/topological-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type {ReadonlyDeep} from 'type-fest';

type TopologicalSortable = ReadonlyDeep<{
name: string;
dependencies?: string[];
}>;

/**
* This sorting algorithm is used to make sure that dependent tables are imported after their dependencies.
*/
export function topologicalSort<T extends TopologicalSortable>(objects: ReadonlyArray<T>): T[] {
// Create an empty result array to store the ordered objects
const result: T[] = [];
// Create a set to track visited objects during the DFS
const visited = new Set<string>();
const objectsByName = new Map<string, T>();

for (const object of objects) {
objectsByName.set(object.name, object);
}

// Helper function to perform DFS
function dfs(name: string): void {
if (visited.has(name)) {
return;
}

const object = objectsByName.get(name);
if (!object) {
return;
}

visited.add(name);

for (const dependency of object.dependencies || []) {
dfs(dependency);
}

result.push(object);
}

// Perform DFS on each object
for (const object of objects) {
dfs(object.name);
}

return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
EditAutomationData,
Page
} from './automations-repository';
import type {ExclusifyUnion} from 'type-fest';
import type {ExclusifyUnion, ReadonlyDeep} from 'type-fest';

const HOUR_MS = 60 * 60 * 1000;

Expand Down Expand Up @@ -241,7 +241,7 @@ function findFirstActionRevision(database: DatabaseSync, memberStatus: 'free' |
}

function getReadyAtForAction(
action: Pick<NextActionRevisionRow, 'action_id' | 'type' | 'wait_hours'>,
action: ReadonlyDeep<Pick<NextActionRevisionRow, 'action_id' | 'type' | 'wait_hours'>>,
now: Readonly<Date>
): Date {
switch (action.type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,30 @@ const buildSendEmailAction = (dataOverrides = {}) => ({
}
});

describe('automations API edit validation', function () {
const automationId = ObjectId().toHexString();
describe('automations API', function () {
describe('edit', function () {
const automationId = ObjectId().toHexString();

it('rejects activating an automation with an empty email subject', async function () {
await assert.rejects(
automationsApi.edit(automationId, {
status: 'active',
actions: [buildSendEmailAction({email_subject: ''})],
edges: []
}),
/subject line/
);
});
it('rejects activating an automation with an empty email subject', async function () {
await assert.rejects(
automationsApi.edit(automationId, {
status: 'active',
actions: [buildSendEmailAction({email_subject: ''})],
edges: []
}),
/subject line/
);
});

it('rejects activating an automation with an empty email body', async function () {
await assert.rejects(
automationsApi.edit(automationId, {
status: 'active',
actions: [buildSendEmailAction({email_lexical: EMPTY_EMAIL_LEXICAL})],
edges: []
}),
/body/
);
it('rejects activating an automation with an empty email body', async function () {
await assert.rejects(
automationsApi.edit(automationId, {
status: 'active',
actions: [buildSendEmailAction({email_lexical: EMPTY_EMAIL_LEXICAL})],
edges: []
}),
/body/
);
});
});
});
Loading