diff --git a/bin/si-api-test/sdf_api_client.ts b/bin/si-api-test/sdf_api_client.ts index 58574886c7..708087443a 100644 --- a/bin/si-api-test/sdf_api_client.ts +++ b/bin/si-api-test/sdf_api_client.ts @@ -13,6 +13,10 @@ interface API_DESCRIPTION { export const ROUTES = { // Change Set Management ------------------------------------------------------ + apply_change_set: { + path: () => "/change_set/apply_change_set", + method: "POST", + }, create_change_set: { path: () => "/change_set/create_change_set", method: "POST", diff --git a/bin/si-api-test/tests/create_and_apply_across_change_sets.ts b/bin/si-api-test/tests/create_and_apply_across_change_sets.ts new file mode 100644 index 0000000000..814e4945eb --- /dev/null +++ b/bin/si-api-test/tests/create_and_apply_across_change_sets.ts @@ -0,0 +1,268 @@ +import assert from "node:assert"; +import { SdfApiClient } from "../sdf_api_client.ts"; + +export default async function create_and_and_apply_across_change_sets( + sdfApiClient: SdfApiClient, +) { + return await create_and_and_apply_across_change_sets_inner(sdfApiClient); +} + +async function create_and_and_apply_across_change_sets_inner( + sdf: SdfApiClient, +) { + sdf.listenForDVUs(); + console.log("cleaning up any existing components..."); + await cleanupHead(sdf); + console.log("creating changesets with the test component in them..."); + let changeSetIds: string[] = []; + let err: Error | undefined = undefined; + try { + for (let i = 0; i < 10; i++) { + const changeSetId = await createChangeSet(sdf); + changeSetIds.push(changeSetId); + + const testResourceActionsVariantId = await getTestSchemaVariantId( + sdf, + changeSetId, + ); + + await createComponent( + sdf, + changeSetId, + testResourceActionsVariantId, + 0, + 0, + ); + } + + await sdf.waitForDVUs(2000); + console.log("Done! Running an apply..."); + await sdf.call({ + route: "apply_change_set", + body: { + visibility_change_set_pk: changeSetIds.pop(), + }, + }); + + await sdf.waitForDVUs(2000); + console.log("verifying and cleaning up change sets..."); + for (var changeSetId of changeSetIds) { + const diagram = await getDiagram(sdf, changeSetId); + + await sdf.call({ + route: "abandon_change_set", + body: { + changeSetId, + }, + }); + + assert( + diagram.components.length == 2, + `Expected diagram to have two components after apply, found ${diagram.components.length}`, + ); + } + } catch (e) { + err = e; + } + await cleanupHead(sdf); + if (err) { + throw err; + } +} + +async function cleanupHead(sdf: SdfApiClient): Promise { + const cleanupChangeSetId = await createChangeSet(sdf); + const diagram = await getDiagram(sdf, cleanupChangeSetId); + const currentComponentIds = diagram.components.map((c) => c.id); + if (currentComponentIds) { + const deleteComponentPayload = { + componentIds: currentComponentIds, + forceErase: false, + visibility_change_set_pk: cleanupChangeSetId, + workspaceId: sdf.workspaceId, + }; + await sdf.call({ + route: "delete_component", + body: deleteComponentPayload, + }); + await sdf.call({ + route: "apply_change_set", + body: { + visibility_change_set_pk: cleanupChangeSetId, + }, + }); + } +} + +async function createChangeSet(sdf: SdfApiClient): Promise { + const startTime = new Date(); + const changeSetName = `API_TEST - ${startTime.toISOString()}`; + const data = await sdf.call({ + route: "create_change_set", + body: { + changeSetName, + }, + }); + return data.changeSet.id; +} + +async function getTestSchemaVariantId( + sdf: SdfApiClient, + changeSetId: string, +): Promise { + const schemaVariants = await getSchemaVariants(sdf, changeSetId); + + const testResourceActionsVariant = await extractSchemaVariant( + schemaVariants, + "TestResourceActions", + "", + ); + return testResourceActionsVariant.schemaVariantId; +} + +function extractSchemaVariant( + schemaVariants: any[], + schemaName: string, + category?: string, +) { + const variant = schemaVariants.find( + (sv) => + sv.schemaName === schemaName && (!category || sv.category === category), + ); + + return variant; +} + +async function getSchemaVariants(sdf: SdfApiClient, changeSetId: string) { + const schemaVariants = await sdf.call({ + route: "schema_variants", + routeVars: { + workspaceId: sdf.workspaceId, + changeSetId, + }, + }); + assert( + Array.isArray(schemaVariants), + "List schema variants should return an array", + ); + + return schemaVariants; +} + +async function getDiagram( + sdf: SdfApiClient, + changeSetId: string, +): Promise<{ components: any[]; edges: any[] }> { + const diagram = await sdf.call({ + route: "get_diagram", + routeVars: { + workspaceId: sdf.workspaceId, + changeSetId, + }, + }); + + assert( + Array.isArray(diagram?.components), + "Expected components list on the diagram", + ); + assert(Array.isArray(diagram?.edges), "Expected edges list on the diagram"); + + return diagram; +} + +async function createComponent( + sdf: SdfApiClient, + changeSetId: string, + schemaVariantId: string, + x: number, + y: number, + parentId?: string, +): Promise { + const parentArgs = parentId ? { parentId } : {}; + const payload = { + schemaVariantId, + x: x.toString(), + y: y.toString(), + visibility_change_set_pk: changeSetId, + workspaceId: sdf.workspaceId, + ...parentArgs, + }; + const createResp = await sdf.call({ + route: "create_component", + body: payload, + }); + const componentId = createResp?.componentId; + assert(componentId, "Expected to get a component id after creation"); + + // Run side effect calls + await Promise.all([ + getQualificationSummary(sdf, changeSetId), + getActions(sdf, changeSetId), + getFuncs(sdf, changeSetId), + getPropertyEditor(sdf, changeSetId, componentId), + ]); + + return componentId; +} + +async function getPropertyEditor( + sdf: SdfApiClient, + changeSetId: string, + componentId: string, +) { + const values = await sdf.call({ + route: "get_property_values", + routeVars: { + componentId, + changeSetId, + }, + }); + assert(typeof values?.values === "object", "Expected prop values"); + assert(typeof values?.childValues === "object", "Expected prop childValues:"); + + const schema = await sdf.call({ + route: "get_property_schema", + routeVars: { + componentId, + changeSetId, + }, + }); + assert(typeof schema?.rootPropId === "string", "Expected rootPropId"); + assert(typeof schema?.props === "object", "Expected props"); + assert(typeof schema?.childProps === "object", "Expected childProps list"); + + return { + values, + schema, + }; +} + +async function getQualificationSummary(sdf: SdfApiClient, changeSetId: string) { + return await sdf.call({ + route: "qualification_summary", + routeVars: { + workspaceId: sdf.workspaceId, + changeSetId, + }, + }); +} + +async function getActions(sdf: SdfApiClient, changeSetId: string) { + return await sdf.call({ + route: "action_list", + routeVars: { + workspaceId: sdf.workspaceId, + changeSetId, + }, + }); +} + +async function getFuncs(sdf: SdfApiClient, changeSetId: string) { + return await sdf.call({ + route: "func_list", + routeVars: { + workspaceId: sdf.workspaceId, + changeSetId, + }, + }); +}