@highstate/backend 0.9.18 → 0.9.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5WVU2AK4.js +1535 -0
- package/dist/chunk-5WVU2AK4.js.map +1 -0
- package/dist/{chunk-OU5OQBLB.js → chunk-I7BWSAN6.js} +3 -28
- package/dist/{chunk-OU5OQBLB.js.map → chunk-I7BWSAN6.js.map} +1 -1
- package/dist/chunk-VB4YL327.js +139 -0
- package/dist/chunk-VB4YL327.js.map +1 -0
- package/dist/database/local/prisma.config.js +26 -0
- package/dist/database/local/prisma.config.js.map +1 -0
- package/dist/highstate.manifest.json +2 -1
- package/dist/index.js +7587 -7291
- package/dist/index.js.map +1 -1
- package/dist/library/package-resolution-worker.js +1 -1
- package/dist/library/package-resolution-worker.js.map +1 -1
- package/dist/library/worker/main.js +35 -29
- package/dist/library/worker/main.js.map +1 -1
- package/dist/shared/index.js +2 -2
- package/package.json +18 -9
- package/prisma/backend/_schema/layout.prisma +7 -0
- package/prisma/backend/_schema/library.prisma +17 -0
- package/prisma/backend/_schema/project.prisma +101 -0
- package/prisma/backend/_schema/pulumi.prisma +17 -0
- package/prisma/backend/postgresql/main.prisma +17 -0
- package/prisma/backend/sqlite/main.prisma +17 -0
- package/prisma/backend/sqlite/migrations/20250817070609_initiial/migration.sql +34 -0
- package/prisma/backend/sqlite/migrations/20250817104948_add_fields/migration.sql +59 -0
- package/prisma/backend/sqlite/migrations/20250818082732_add_models/migration.sql +41 -0
- package/prisma/backend/sqlite/migrations/20250818083106_a/migration.sql +19 -0
- package/prisma/backend/sqlite/migrations/20250818101945_hi/migration.sql +1 -0
- package/prisma/backend/sqlite/migrations/20250819082315_a/migration.sql +5 -0
- package/prisma/backend/sqlite/migrations/migration_lock.toml +3 -0
- package/prisma/project/api-key.prisma +27 -0
- package/prisma/project/artifact.prisma +52 -0
- package/prisma/project/custom-status.prisma +46 -0
- package/prisma/project/evaluation.prisma +35 -0
- package/prisma/project/instance.prisma +160 -0
- package/prisma/project/layout.prisma +23 -0
- package/prisma/project/lock.prisma +18 -0
- package/prisma/project/main.prisma +17 -0
- package/prisma/project/migrations/20250816081310_initial/migration.sql +300 -0
- package/prisma/project/migrations/20250816082523_test/migration.sql +72 -0
- package/prisma/project/migrations/20250818065643_update/migration.sql +42 -0
- package/prisma/project/migrations/20250818070758_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818070913_a/migration.sql +8 -0
- package/prisma/project/migrations/20250818082720_add_motels/migration.sql +11 -0
- package/prisma/project/migrations/20250818112523_hello/migration.sql +35 -0
- package/prisma/project/migrations/20250819082305_a/migration.sql +14 -0
- package/prisma/project/migrations/20250819165004_add_missing_fields/migration.sql +216 -0
- package/prisma/project/migrations/20250819171309_a/migration.sql +22 -0
- package/prisma/project/migrations/20250820113949_a/migration.sql +66 -0
- package/prisma/project/migrations/20250820144256_b/migration.sql +31 -0
- package/prisma/project/migrations/20250820145547_a/migration.sql +24 -0
- package/prisma/project/migrations/20250820182517_b/migration.sql +2 -0
- package/prisma/project/migrations/20250821172324_a/migration.sql +2 -0
- package/prisma/project/migrations/20250822081339_a/migration.sql +219 -0
- package/prisma/project/migrations/20250822083742_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822105134_boom/migration.sql +1 -0
- package/prisma/project/migrations/20250822141028_b/migration.sql +1 -0
- package/prisma/project/migrations/20250822142342_b/migration.sql +16 -0
- package/prisma/project/migrations/20250824072720_a/migration.sql +1 -0
- package/prisma/project/migrations/20250824093656_b/migration.sql +21 -0
- package/prisma/project/migrations/20250825082518_a/migration.sql +1 -0
- package/prisma/project/migrations/20250825085343_b/migration.sql +1 -0
- package/prisma/project/migrations/20250825091312_a/migration.sql +1 -0
- package/prisma/project/migrations/20250903095431_hi/migration.sql +44 -0
- package/prisma/project/migrations/20250903174255_a/migration.sql +24 -0
- package/prisma/project/migrations/20250908095205_hi/migration.sql +18 -0
- package/prisma/project/migrations/20250909155857_hi/migration.sql +15 -0
- package/prisma/project/migrations/migration_lock.toml +3 -0
- package/prisma/project/model.prisma +37 -0
- package/prisma/project/operation.prisma +148 -0
- package/prisma/project/page.prisma +41 -0
- package/prisma/project/secret.prisma +42 -0
- package/prisma/project/service-account.prisma +36 -0
- package/prisma/project/terminal.prisma +90 -0
- package/prisma/project/trigger.prisma +31 -0
- package/prisma/project/unlock-method.prisma +32 -0
- package/prisma/project/worker.prisma +138 -0
- package/src/artifact/abstractions.ts +13 -13
- package/src/artifact/encryption.ts +30 -54
- package/src/artifact/factory.ts +6 -9
- package/src/artifact/local.ts +33 -46
- package/src/business/api-key.ts +24 -36
- package/src/business/artifact.test.ts +978 -0
- package/src/business/artifact.ts +136 -216
- package/src/business/evaluation.ts +328 -0
- package/src/business/index.ts +5 -2
- package/src/business/instance-lock.test.ts +1060 -0
- package/src/business/instance-lock.ts +387 -78
- package/src/business/instance-state.test.ts +735 -0
- package/src/business/instance-state.ts +582 -337
- package/src/business/operation.test.ts +439 -0
- package/src/business/operation.ts +174 -208
- package/src/business/project-model.ts +258 -0
- package/src/business/project-unlock.ts +168 -126
- package/src/business/project.ts +287 -179
- package/src/business/secret.test.ts +465 -130
- package/src/business/secret.ts +186 -217
- package/src/business/settings.test.ts +695 -0
- package/src/business/settings.ts +855 -0
- package/src/business/terminal-session.ts +90 -0
- package/src/business/unit-extra.test.ts +539 -0
- package/src/business/unit-extra.ts +160 -0
- package/src/business/worker.test.ts +356 -579
- package/src/business/worker.ts +238 -339
- package/src/common/codebase.ts +65 -0
- package/src/common/index.ts +3 -5
- package/src/common/logger.ts +5 -0
- package/src/common/utils.ts +4 -3
- package/src/config.ts +10 -11
- package/src/database/_generated/backend/postgresql/client.ts +72 -0
- package/src/database/_generated/backend/postgresql/commonInputTypes.ts +350 -0
- package/src/database/_generated/backend/postgresql/enums.ts +13 -0
- package/src/database/_generated/backend/postgresql/internal/class.ts +320 -0
- package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +1238 -0
- package/src/database/_generated/backend/postgresql/models/Library.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/Project.ts +2175 -0
- package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +1602 -0
- package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +1263 -0
- package/src/database/_generated/backend/postgresql/models/UserWorkspaseLayout.ts +1065 -0
- package/src/database/_generated/backend/postgresql/models.ts +16 -0
- package/src/database/_generated/backend/postgresql/pjtg.ts +182 -0
- package/src/database/_generated/backend/sqlite/client.ts +72 -0
- package/src/database/_generated/backend/sqlite/commonInputTypes.ts +331 -0
- package/src/database/_generated/backend/sqlite/enums.ts +13 -0
- package/src/database/_generated/backend/sqlite/internal/class.ts +318 -0
- package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +1207 -0
- package/src/database/_generated/backend/sqlite/models/Library.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/Project.ts +2169 -0
- package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +1599 -0
- package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +1261 -0
- package/src/database/_generated/backend/sqlite/models/UserWorkspaseLayout.ts +1063 -0
- package/src/database/_generated/backend/sqlite/models.ts +16 -0
- package/src/database/_generated/backend/sqlite/pjtg.ts +182 -0
- package/src/database/_generated/project/client.ts +204 -0
- package/src/database/_generated/project/commonInputTypes.ts +827 -0
- package/src/database/_generated/project/enums.ts +104 -0
- package/src/database/_generated/project/internal/class.ts +479 -0
- package/src/database/_generated/project/internal/prismaNamespace.ts +2974 -0
- package/src/database/_generated/project/models/ApiKey.ts +1506 -0
- package/src/database/_generated/project/models/Artifact.ts +2051 -0
- package/src/database/_generated/project/models/HubModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceCustomStatus.ts +1713 -0
- package/src/database/_generated/project/models/InstanceEvaluationState.ts +1312 -0
- package/src/database/_generated/project/models/InstanceLock.ts +1268 -0
- package/src/database/_generated/project/models/InstanceModel.ts +1125 -0
- package/src/database/_generated/project/models/InstanceOperationState.ts +1707 -0
- package/src/database/_generated/project/models/InstanceState.ts +4613 -0
- package/src/database/_generated/project/models/Operation.ts +1647 -0
- package/src/database/_generated/project/models/OperationLog.ts +1455 -0
- package/src/database/_generated/project/models/Page.ts +1838 -0
- package/src/database/_generated/project/models/Secret.ts +1692 -0
- package/src/database/_generated/project/models/ServiceAccount.ts +2165 -0
- package/src/database/_generated/project/models/Terminal.ts +2038 -0
- package/src/database/_generated/project/models/TerminalSession.ts +1454 -0
- package/src/database/_generated/project/models/TerminalSessionLog.ts +1280 -0
- package/src/database/_generated/project/models/Trigger.ts +1430 -0
- package/src/database/_generated/project/models/UnlockMethod.ts +1220 -0
- package/src/database/_generated/project/models/UserCompositeViewport.ts +1280 -0
- package/src/database/_generated/project/models/UserProjectViewport.ts +1059 -0
- package/src/database/_generated/project/models/Worker.ts +1459 -0
- package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1524 -0
- package/src/database/_generated/project/models/WorkerVersion.ts +1974 -0
- package/src/database/_generated/project/models/WorkerVersionLog.ts +1318 -0
- package/src/database/_generated/project/models.ts +35 -0
- package/src/database/_generated/project/pjtg.ts +182 -0
- package/src/database/abstractions.ts +19 -0
- package/src/database/factory.ts +37 -0
- package/src/database/index.ts +6 -0
- package/src/database/local/backend.ts +134 -0
- package/src/database/local/index.ts +3 -0
- package/src/database/local/meta.ts +46 -0
- package/src/database/local/prisma.config.ts +25 -0
- package/src/database/local/project.ts +39 -0
- package/src/database/manager.ts +181 -0
- package/src/database/migrate.ts +35 -0
- package/src/database/prisma.ts +56 -0
- package/src/database/well-known.ts +38 -0
- package/src/index.ts +4 -4
- package/src/library/abstractions.ts +3 -5
- package/src/library/factory.ts +1 -1
- package/src/library/local.ts +81 -26
- package/src/library/package-resolution-worker.ts +1 -1
- package/src/library/worker/evaluator.ts +40 -23
- package/src/library/worker/loader.lite.ts +1 -1
- package/src/library/worker/main.ts +3 -10
- package/src/library/worker/protocol.ts +0 -1
- package/src/lock/index.ts +0 -1
- package/src/lock/manager.ts +0 -10
- package/src/orchestrator/manager.ts +190 -104
- package/src/orchestrator/operation-context.ts +357 -0
- package/src/orchestrator/operation-plan.destroy.test.md +357 -0
- package/src/orchestrator/operation-plan.destroy.test.ts +775 -0
- package/src/orchestrator/operation-plan.fixtures.ts +213 -0
- package/src/orchestrator/operation-plan.md +198 -0
- package/src/orchestrator/operation-plan.refresh.test.md +199 -0
- package/src/orchestrator/operation-plan.refresh.test.ts +367 -0
- package/src/orchestrator/operation-plan.ts +709 -0
- package/src/orchestrator/operation-plan.update.test.md +485 -0
- package/src/orchestrator/operation-plan.update.test.ts +1066 -0
- package/src/orchestrator/operation-workset.ts +233 -578
- package/src/orchestrator/operation.ts +435 -948
- package/src/orchestrator/plan-test-builder.ts +267 -0
- package/src/project-model/abstractions.ts +118 -0
- package/src/project-model/backends/codebase.ts +365 -0
- package/src/project-model/backends/database.ts +440 -0
- package/src/project-model/errors.ts +81 -0
- package/src/project-model/factory.ts +24 -0
- package/src/project-model/index.ts +4 -0
- package/src/project-model/utils.test.ts +544 -0
- package/src/project-model/utils.ts +242 -0
- package/src/pubsub/abstractions.ts +10 -1
- package/src/pubsub/factory.ts +4 -4
- package/src/pubsub/index.ts +1 -0
- package/src/pubsub/manager.ts +29 -13
- package/src/pubsub/memory.ts +31 -0
- package/src/runner/abstractions.ts +33 -41
- package/src/runner/artifact-env.ts +19 -8
- package/src/runner/factory.ts +6 -6
- package/src/runner/force-abort.ts +3 -6
- package/src/runner/local.ts +64 -67
- package/src/runner/pulumi.ts +23 -63
- package/src/services.ts +181 -123
- package/src/shared/models/backend/index.ts +3 -1
- package/src/shared/models/backend/library.ts +9 -1
- package/src/shared/models/backend/project.ts +43 -42
- package/src/shared/models/backend/pulumi.ts +14 -0
- package/src/shared/models/backend/unlock-method.ts +1 -1
- package/src/shared/models/backend/well-known.ts +58 -0
- package/src/shared/models/base.ts +40 -26
- package/src/shared/models/errors.ts +82 -1
- package/src/shared/models/index.ts +3 -2
- package/src/shared/models/prisma.ts +36 -0
- package/src/shared/models/project/api-key.ts +37 -59
- package/src/shared/models/project/artifact.ts +16 -76
- package/src/shared/models/project/custom-status.ts +12 -0
- package/src/shared/models/project/index.ts +8 -7
- package/src/shared/models/project/lock.ts +10 -78
- package/src/shared/models/project/model.ts +19 -1
- package/src/shared/models/project/operation.ts +222 -99
- package/src/shared/models/project/page.ts +37 -48
- package/src/shared/models/project/secret.ts +29 -89
- package/src/shared/models/project/service-account.ts +12 -17
- package/src/shared/models/project/state.ts +100 -407
- package/src/shared/models/project/terminal.ts +75 -88
- package/src/shared/models/project/trigger.ts +13 -49
- package/src/shared/models/project/unlock-method.ts +20 -26
- package/src/shared/models/project/worker.ts +89 -90
- package/src/shared/resolvers/graph-resolver.ts +21 -0
- package/src/shared/resolvers/index.ts +1 -1
- package/src/shared/resolvers/input-hash.ts +24 -14
- package/src/shared/resolvers/input.ts +1 -1
- package/src/shared/resolvers/registry.ts +5 -4
- package/src/shared/resolvers/state.ts +12 -1
- package/src/shared/resolvers/validation.ts +7 -3
- package/src/shared/utils/index.ts +1 -2
- package/src/shared/utils/promise-tracker.ts +30 -3
- package/src/terminal/abstractions.ts +1 -1
- package/src/terminal/docker.ts +3 -3
- package/src/terminal/manager.ts +102 -118
- package/src/test-utils/database.ts +119 -0
- package/src/test-utils/index.ts +2 -0
- package/src/test-utils/services.ts +134 -0
- package/src/unlock/abstractions.ts +5 -23
- package/src/unlock/memory.ts +9 -14
- package/src/worker/abstractions.ts +7 -4
- package/src/worker/docker.ts +14 -19
- package/src/worker/manager.ts +366 -97
- package/dist/chunk-NAAIDR4U.js +0 -8499
- package/dist/chunk-NAAIDR4U.js.map +0 -1
- package/dist/chunk-Y7DXREVO.js +0 -1745
- package/dist/chunk-Y7DXREVO.js.map +0 -1
- package/dist/magic-string.es-5ABAC4JN.js +0 -1292
- package/dist/magic-string.es-5ABAC4JN.js.map +0 -1
- package/src/business/__traces__/secret/update-instance-secrets/create-and-delete-secrets-simultaneously.md +0 -356
- package/src/business/__traces__/secret/update-instance-secrets/create-new-secrets-for-instance.md +0 -274
- package/src/business/__traces__/secret/update-instance-secrets/delete-existing-secrets.md +0 -223
- package/src/business/__traces__/secret/update-instance-secrets/no-op-when-no-changes.md +0 -147
- package/src/business/__traces__/secret/update-instance-secrets/update-existing-secrets.md +0 -280
- package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration-when-other-exists.md +0 -360
- package/src/business/__traces__/worker/update-unit-registrations/add-new-unit-registration.md +0 -215
- package/src/business/__traces__/worker/update-unit-registrations/create-multiple-workers-with-different-identities.md +0 -427
- package/src/business/__traces__/worker/update-unit-registrations/handle-nonexistent-registration-id-gracefully.md +0 -217
- package/src/business/__traces__/worker/update-unit-registrations/no-op-when-no-changes.md +0 -132
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-changes.md +0 -454
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-when-image-version-changes.md +0 -426
- package/src/business/__traces__/worker/update-unit-registrations/recreate-worker-with-same-identity-reuses-service-account.md +0 -372
- package/src/business/__traces__/worker/update-unit-registrations/remove-one-of-multiple-unit-registrations.md +0 -383
- package/src/business/__traces__/worker/update-unit-registrations/remove-unit-registration.md +0 -245
- package/src/business/__traces__/worker/update-unit-registrations/update-existing-unit-registration-when-params-change.md +0 -174
- package/src/business/__traces__/worker/update-unit-registrations/update-params-and-image-simultaneously.md +0 -432
- package/src/business/__traces__/worker/update-unit-registrations/worker-with-multiple-registrations-not-deleted-when-one-removed.md +0 -220
- package/src/business/backend-unlock.ts +0 -10
- package/src/common/clock.ts +0 -18
- package/src/common/performance.ts +0 -44
- package/src/common/random.ts +0 -68
- package/src/common/test/index.ts +0 -2
- package/src/common/test/render.ts +0 -98
- package/src/common/test/tracer.ts +0 -359
- package/src/hotstate/abstractions.ts +0 -48
- package/src/hotstate/factory.ts +0 -17
- package/src/hotstate/index.ts +0 -3
- package/src/hotstate/manager.ts +0 -192
- package/src/hotstate/memory.ts +0 -100
- package/src/hotstate/validation.ts +0 -100
- package/src/lock/test.ts +0 -108
- package/src/project/abstractions.ts +0 -78
- package/src/project/evaluation.ts +0 -248
- package/src/project/factory.ts +0 -11
- package/src/project/index.ts +0 -3
- package/src/project/local.ts +0 -417
- package/src/pubsub/local.ts +0 -36
- package/src/pubsub/validation.ts +0 -33
- package/src/shared/utils/args.ts +0 -25
- package/src/state/abstractions.ts +0 -289
- package/src/state/encryption.ts +0 -98
- package/src/state/factory.ts +0 -20
- package/src/state/index.ts +0 -7
- package/src/state/local/backend.ts +0 -106
- package/src/state/local/collection.ts +0 -361
- package/src/state/local/index.ts +0 -2
- package/src/state/manager.ts +0 -890
- package/src/state/memory/backend.ts +0 -70
- package/src/state/memory/collection.ts +0 -270
- package/src/state/memory/index.ts +0 -2
- package/src/state/repository/index.ts +0 -2
- package/src/state/repository/repository.index.ts +0 -193
- package/src/state/repository/repository.ts +0 -507
- package/src/state/test.ts +0 -457
- /package/src/{state → database/local}/keyring.ts +0 -0
|
@@ -1,251 +1,217 @@
|
|
|
1
|
+
import type { InstanceId } from "@highstate/contract"
|
|
1
2
|
import type { Logger } from "pino"
|
|
3
|
+
import type { DatabaseManager, Operation, OperationStatus, OperationUpdateInput } from "../database"
|
|
2
4
|
import type { PubSubManager } from "../pubsub"
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
import { ulid } from "ulid"
|
|
6
|
+
import {
|
|
7
|
+
type OperationMeta,
|
|
8
|
+
OperationNotFoundError,
|
|
9
|
+
type OperationOptions,
|
|
10
|
+
type OperationType,
|
|
11
|
+
} from "../shared"
|
|
7
12
|
|
|
8
13
|
export class OperationService {
|
|
9
14
|
constructor(
|
|
10
|
-
private readonly
|
|
11
|
-
private readonly hotStateManager: HotStateManager,
|
|
15
|
+
private readonly database: DatabaseManager,
|
|
12
16
|
private readonly pubsubManager: PubSubManager,
|
|
13
17
|
private readonly logger: Logger,
|
|
14
18
|
) {}
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new operation in the database.
|
|
22
|
+
*
|
|
23
|
+
* @param projectId The project ID to which the operation belongs.
|
|
24
|
+
* @param meta The operation metadata.
|
|
25
|
+
* @param type The operation type.
|
|
26
|
+
* @param requestedInstanceIds The instance IDs that were explicitly requested.
|
|
27
|
+
* @param options The operation options.
|
|
28
|
+
* @returns The created operation.
|
|
29
|
+
*/
|
|
30
|
+
async createOperation(
|
|
31
|
+
projectId: string,
|
|
32
|
+
meta: OperationMeta,
|
|
33
|
+
type: OperationType,
|
|
34
|
+
requestedInstanceIds: InstanceId[],
|
|
35
|
+
options: OperationOptions,
|
|
36
|
+
): Promise<Operation> {
|
|
37
|
+
const database = await this.database.forProject(projectId)
|
|
38
|
+
|
|
39
|
+
const operation = await database.operation.create({
|
|
40
|
+
data: {
|
|
41
|
+
meta,
|
|
42
|
+
type,
|
|
43
|
+
options,
|
|
44
|
+
requestedInstanceIds,
|
|
45
|
+
startedAt: new Date(),
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
await this.pubsubManager.publish(["operation", projectId], {
|
|
50
|
+
type: "updated",
|
|
51
|
+
operation,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
this.logger.info({ projectId, operationId: operation.id }, "created operation")
|
|
55
|
+
return operation
|
|
56
|
+
}
|
|
26
57
|
|
|
27
|
-
|
|
28
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Updates an existing operation in the database.
|
|
60
|
+
*
|
|
61
|
+
* @param projectId The project ID containing the operation.
|
|
62
|
+
* @param operationId The operation ID to update.
|
|
63
|
+
* @param updates The updates to apply.
|
|
64
|
+
* @returns The updated operation.
|
|
65
|
+
*/
|
|
66
|
+
async updateOperation(
|
|
67
|
+
projectId: string,
|
|
68
|
+
operationId: string,
|
|
69
|
+
updates: OperationUpdateInput,
|
|
70
|
+
): Promise<Operation> {
|
|
71
|
+
const database = await this.database.forProject(projectId)
|
|
72
|
+
|
|
73
|
+
const operation = await database.operation.update({
|
|
74
|
+
where: { id: operationId },
|
|
75
|
+
data: updates,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
await this.pubsubManager.publish(["operation", projectId], {
|
|
79
|
+
type: "updated",
|
|
80
|
+
operation,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
this.logger.info({ projectId, operationId }, "updated operation")
|
|
84
|
+
return operation
|
|
29
85
|
}
|
|
30
86
|
|
|
31
87
|
/**
|
|
32
|
-
*
|
|
88
|
+
* Gets an operation by ID.
|
|
33
89
|
*
|
|
34
|
-
* @param projectId The project ID
|
|
35
|
-
* @param
|
|
90
|
+
* @param projectId The project ID containing the operation.
|
|
91
|
+
* @param operationId The operation ID.
|
|
92
|
+
* @returns The operation or undefined if not found.
|
|
36
93
|
*/
|
|
37
|
-
async
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
if (isFinalOperationStatus(operation.status)) {
|
|
41
|
-
await this.hotStateManager.hdel(["active-operations", projectId], operation.id)
|
|
42
|
-
} else {
|
|
43
|
-
await this.hotStateManager.hset(["active-operations", projectId], operation.id, operation)
|
|
44
|
-
}
|
|
45
|
-
} catch (error) {
|
|
46
|
-
this.logger.error(
|
|
47
|
-
{ projectId, operationId: operation.id, error },
|
|
48
|
-
`failed to update operation with ID "%s" in project "%s" in hot state`,
|
|
49
|
-
operation.id,
|
|
50
|
-
projectId,
|
|
51
|
-
)
|
|
52
|
-
}
|
|
94
|
+
async getOperation(projectId: string, operationId: string): Promise<Operation | undefined> {
|
|
95
|
+
const database = await this.database.forProject(projectId)
|
|
53
96
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
await this.pubsubManager.publish(["operation", operation.id], {
|
|
77
|
-
type: "updated",
|
|
78
|
-
operation,
|
|
79
|
-
})
|
|
80
|
-
} catch (error) {
|
|
81
|
-
this.logger.error(
|
|
82
|
-
{ projectId, operationId: operation.id, error },
|
|
83
|
-
`failed to publish operation update for operation with ID "%s" in project "%s"`,
|
|
84
|
-
operation.id,
|
|
85
|
-
projectId,
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
this.logger.info(
|
|
90
|
-
{ projectId, operationId: operation.id },
|
|
91
|
-
`updated operation with ID "%s" in project "%s"`,
|
|
92
|
-
operation.id,
|
|
93
|
-
projectId,
|
|
94
|
-
)
|
|
95
|
-
} catch (error) {
|
|
96
|
-
this.logger.error(
|
|
97
|
-
{ projectId, operationId: operation.id, error },
|
|
98
|
-
`failed to update operation with ID "%s" in project "%s"`,
|
|
99
|
-
operation.id,
|
|
100
|
-
projectId,
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
throw new Error(`Failed to update operation "${operation.id}" in project "${projectId}"`, {
|
|
104
|
-
cause: error,
|
|
105
|
-
})
|
|
106
|
-
}
|
|
97
|
+
const operation = await database.operation.findUnique({
|
|
98
|
+
where: { id: operationId },
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
return operation ?? undefined
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Gets all operations for a project.
|
|
106
|
+
*
|
|
107
|
+
* @param projectId The project ID.
|
|
108
|
+
* @param limit Optional limit on number of operations to return.
|
|
109
|
+
* @returns Array of operations.
|
|
110
|
+
*/
|
|
111
|
+
async getOperations(projectId: string, limit?: number): Promise<Operation[]> {
|
|
112
|
+
const database = await this.database.forProject(projectId)
|
|
113
|
+
|
|
114
|
+
return await database.operation.findMany({
|
|
115
|
+
orderBy: { startedAt: "desc" },
|
|
116
|
+
take: limit,
|
|
117
|
+
})
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
/**
|
|
110
|
-
* Appends
|
|
111
|
-
* Creates all necessary indexes to retrieve the logs by instance ID.
|
|
121
|
+
* Appends log for a specific operation.
|
|
112
122
|
*
|
|
113
|
-
* @param projectId The ID of the project to persist the
|
|
114
|
-
* @param operationId The ID of the operation to persist the
|
|
115
|
-
* @param
|
|
116
|
-
* @param
|
|
123
|
+
* @param projectId The ID of the project to persist the log for.
|
|
124
|
+
* @param operationId The ID of the operation to persist the log for.
|
|
125
|
+
* @param stateId The ID of the instance state that produced the log (optional).
|
|
126
|
+
* @param content The log content to append.
|
|
117
127
|
*/
|
|
118
|
-
async
|
|
128
|
+
async appendLog(
|
|
119
129
|
projectId: string,
|
|
120
130
|
operationId: string,
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
stateId: string | null,
|
|
132
|
+
content: string,
|
|
123
133
|
): Promise<void> {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
// 1. publish logs to the pubsub system, ignore errors
|
|
127
|
-
try {
|
|
128
|
-
const events = instanceIds.flatMap(instanceId =>
|
|
129
|
-
records.map(
|
|
130
|
-
record => [[`operation-instance-log`, operationId, instanceId], record] as const,
|
|
131
|
-
),
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
await this.pubsubManager.publishMany(events)
|
|
135
|
-
} catch (error) {
|
|
136
|
-
this.logger.error(
|
|
137
|
-
{ projectId, operationId, instanceIds, error },
|
|
138
|
-
`failed to publish logs for operation "%s" in project "%s"`,
|
|
139
|
-
operationId,
|
|
140
|
-
projectId,
|
|
141
|
-
)
|
|
142
|
-
}
|
|
134
|
+
const database = await this.database.forProject(projectId)
|
|
143
135
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
operationId,
|
|
153
|
-
projectId,
|
|
154
|
-
)
|
|
136
|
+
// verify operation exists
|
|
137
|
+
const operation = await database.operation.findUnique({
|
|
138
|
+
where: { id: operationId },
|
|
139
|
+
select: { id: true, finishedAt: true },
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (!operation) {
|
|
143
|
+
throw new OperationNotFoundError(projectId, operationId)
|
|
155
144
|
}
|
|
156
145
|
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// insert logs into the operation log repository
|
|
162
|
-
await this.stateManager
|
|
163
|
-
.getOperationLogRepository(projectId, operationId)
|
|
164
|
-
.putMany(records, batch)
|
|
165
|
-
|
|
166
|
-
for (const instanceId of instanceIds) {
|
|
167
|
-
// create an index for the instance logs
|
|
168
|
-
await this.stateManager
|
|
169
|
-
.getInstanceLogIndexRepository(projectId, operationId, instanceId)
|
|
170
|
-
.indexRepository.putMany(
|
|
171
|
-
records.map(([id]) => [id, SAME_KEY]),
|
|
172
|
-
batch,
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
await batch.write()
|
|
177
|
-
} catch (error) {
|
|
178
|
-
this.logger.error(
|
|
179
|
-
{ projectId, operationId, instanceIds, error },
|
|
180
|
-
`failed to persist instance logs for operation "%s" in project "%s"`,
|
|
146
|
+
// store logs in database
|
|
147
|
+
const entry = await database.operationLog.create({
|
|
148
|
+
data: {
|
|
149
|
+
id: ulid(),
|
|
181
150
|
operationId,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
151
|
+
stateId,
|
|
152
|
+
content,
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// publish logs via pubsub - only for logs with stateId (not system logs)
|
|
157
|
+
if (stateId) {
|
|
158
|
+
await this.pubsubManager.publish(["operation-instance-log", operationId, stateId], entry)
|
|
189
159
|
}
|
|
190
160
|
}
|
|
191
161
|
|
|
192
162
|
/**
|
|
193
|
-
* Retrieves logs for a specific operation and instance.
|
|
194
|
-
* First tries to get logs from the hot state, then falls back to the state repository.
|
|
163
|
+
* Retrieves logs for a specific operation and optionally an instance.
|
|
195
164
|
*
|
|
196
165
|
* @param projectId The ID of the project to retrieve logs for.
|
|
197
166
|
* @param operationId The ID of the operation to retrieve logs for.
|
|
198
|
-
* @param
|
|
199
|
-
* @returns
|
|
167
|
+
* @param stateId Optional instance state ID to filter logs.
|
|
168
|
+
* @returns Array of log entries.
|
|
200
169
|
*/
|
|
201
|
-
async
|
|
170
|
+
async getOperationLogs(
|
|
202
171
|
projectId: string,
|
|
203
172
|
operationId: string,
|
|
204
|
-
|
|
205
|
-
): Promise<
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
const logs = await this.hotStateManager.hgetall(["operation-logs", projectId])
|
|
209
|
-
|
|
210
|
-
if (logs.length > 0) {
|
|
211
|
-
// if there are logs, assume they are up-to-date
|
|
212
|
-
return logs
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// otherwise, check if the operation is active to determine if logs are expected
|
|
216
|
-
const hasActiveOperation = await this.hotStateManager.hexists(
|
|
217
|
-
["active-operations", projectId],
|
|
218
|
-
operationId,
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
if (hasActiveOperation) {
|
|
222
|
-
// if the operation is active, there is just no logs yet
|
|
223
|
-
return []
|
|
224
|
-
}
|
|
225
|
-
} catch (error) {
|
|
226
|
-
this.logger.error(
|
|
227
|
-
{ projectId, operationId, instanceId, error },
|
|
228
|
-
`failed to get logs for operation "%s" in project "%s"`,
|
|
229
|
-
operationId,
|
|
230
|
-
projectId,
|
|
231
|
-
)
|
|
232
|
-
}
|
|
173
|
+
stateId?: string,
|
|
174
|
+
): Promise<Array<{ id: string; stateId: string | null; content: string }>> {
|
|
175
|
+
const database = await this.database.forProject(projectId)
|
|
233
176
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
// if no logs are found in the hot state, load them from the state repository
|
|
237
|
-
return await this.stateManager
|
|
238
|
-
.getInstanceLogIndexRepository(projectId, operationId, instanceId)
|
|
239
|
-
.getAll()
|
|
240
|
-
} catch (error) {
|
|
241
|
-
this.logger.error(
|
|
242
|
-
{ projectId, operationId, instanceId, error },
|
|
243
|
-
`failed to get logs for operation "%s" in project "%s"`,
|
|
177
|
+
const logs = await database.operationLog.findMany({
|
|
178
|
+
where: {
|
|
244
179
|
operationId,
|
|
245
|
-
|
|
246
|
-
|
|
180
|
+
...(stateId ? { stateId } : {}),
|
|
181
|
+
},
|
|
182
|
+
orderBy: { id: "asc" },
|
|
183
|
+
select: {
|
|
184
|
+
id: true,
|
|
185
|
+
stateId: true,
|
|
186
|
+
content: true,
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return logs
|
|
191
|
+
}
|
|
247
192
|
|
|
248
|
-
|
|
249
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Marks an operation as finished with given status.
|
|
195
|
+
*
|
|
196
|
+
* @param projectId The project ID containing the operation.
|
|
197
|
+
* @param operationId The operation ID to complete.
|
|
198
|
+
* @returns The updated operation.
|
|
199
|
+
*/
|
|
200
|
+
async markOperationFinished(
|
|
201
|
+
projectId: string,
|
|
202
|
+
operationId: string,
|
|
203
|
+
status: OperationStatus,
|
|
204
|
+
): Promise<Operation> {
|
|
205
|
+
const result = await this.updateOperation(projectId, operationId, {
|
|
206
|
+
status,
|
|
207
|
+
finishedAt: new Date(),
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
this.logger.info(
|
|
211
|
+
{ projectId, operationId, status },
|
|
212
|
+
`marked operation as finished with status "${status}"`,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return result
|
|
250
216
|
}
|
|
251
217
|
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type { InstanceModel } from "@highstate/contract"
|
|
2
|
+
import type { Logger } from "pino"
|
|
3
|
+
import type { DatabaseManager } from "../database"
|
|
4
|
+
import type { LibraryBackend } from "../library"
|
|
5
|
+
import type { ProjectModelBackend } from "../project-model"
|
|
6
|
+
import type { InstanceStateService } from "./instance-state"
|
|
7
|
+
import type { ProjectUnlockService } from "./project-unlock"
|
|
8
|
+
import { isNonNullish } from "remeda"
|
|
9
|
+
import {
|
|
10
|
+
type FullProjectModel,
|
|
11
|
+
forSchema,
|
|
12
|
+
InputResolver,
|
|
13
|
+
type InputResolverNode,
|
|
14
|
+
ProjectNotFoundError,
|
|
15
|
+
type ProjectOutput,
|
|
16
|
+
projectOutputSchema,
|
|
17
|
+
type ResolvedInstanceInput,
|
|
18
|
+
} from "../shared"
|
|
19
|
+
|
|
20
|
+
export type GetProjectModelOptions = {
|
|
21
|
+
/**
|
|
22
|
+
* Whether to include virtual instances in the model.
|
|
23
|
+
*
|
|
24
|
+
* By default, virtual instances are not included.
|
|
25
|
+
*/
|
|
26
|
+
includeVirtualInstances?: boolean
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether to include ghost instances in the model.
|
|
30
|
+
*
|
|
31
|
+
* By default, ghost instances are not included.
|
|
32
|
+
*/
|
|
33
|
+
includeGhostInstances?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class ProjectModelService {
|
|
37
|
+
constructor(
|
|
38
|
+
private readonly database: DatabaseManager,
|
|
39
|
+
private readonly libraryBackend: LibraryBackend,
|
|
40
|
+
private readonly instanceStateService: InstanceStateService,
|
|
41
|
+
private readonly projectModelBackends: Record<string, ProjectModelBackend>,
|
|
42
|
+
private readonly projectUnlockService: ProjectUnlockService,
|
|
43
|
+
private readonly logger: Logger,
|
|
44
|
+
) {
|
|
45
|
+
this.projectUnlockService.registerUnlockTask(
|
|
46
|
+
//
|
|
47
|
+
"sync-instance-states",
|
|
48
|
+
projectId => this.syncInstanceStates(projectId),
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the project model containing instances, hubs + virtual instances and ghost instances if requested.
|
|
54
|
+
*
|
|
55
|
+
* @param projectId The ID of the project to get the model for.
|
|
56
|
+
* @param options Options to control the model retrieval.
|
|
57
|
+
*/
|
|
58
|
+
async getProjectModel(
|
|
59
|
+
projectId: string,
|
|
60
|
+
{ includeVirtualInstances = false, includeGhostInstances = false }: GetProjectModelOptions = {},
|
|
61
|
+
): Promise<[projectModel: FullProjectModel, project: ProjectOutput]> {
|
|
62
|
+
const { project, backend, spec } = await this.getProjectWithBackend(projectId)
|
|
63
|
+
|
|
64
|
+
// get base model from storage backend
|
|
65
|
+
const { instances, hubs } = await backend.getProjectModel(project, spec)
|
|
66
|
+
|
|
67
|
+
return [
|
|
68
|
+
{
|
|
69
|
+
instances,
|
|
70
|
+
hubs,
|
|
71
|
+
virtualInstances: includeVirtualInstances ? await this.getVirtualInstances(projectId) : [],
|
|
72
|
+
ghostInstances: includeGhostInstances
|
|
73
|
+
? await this.getGhostInstances(
|
|
74
|
+
projectId,
|
|
75
|
+
instances.map(instance => instance.id),
|
|
76
|
+
)
|
|
77
|
+
: [],
|
|
78
|
+
},
|
|
79
|
+
project,
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Resolve a project by loading all dependencies and processing input resolution.
|
|
85
|
+
* Does not load virtual instances.
|
|
86
|
+
*
|
|
87
|
+
* @param projectId The ID of the project to resolve.
|
|
88
|
+
*/
|
|
89
|
+
async resolveProject(projectId: string) {
|
|
90
|
+
const [[{ instances, hubs }, project], states] = await Promise.all([
|
|
91
|
+
this.getProjectModel(projectId),
|
|
92
|
+
this.instanceStateService.getInstanceStates(projectId, { includeEvaluationState: true }),
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
const library = await this.libraryBackend.loadLibrary(project.libraryId)
|
|
96
|
+
|
|
97
|
+
const filteredInstances = instances.filter(instance => instance.type in library.components)
|
|
98
|
+
const stateMap = new Map(states.map(state => [state.id, state]))
|
|
99
|
+
|
|
100
|
+
const inputResolverNodes = new Map<string, InputResolverNode>()
|
|
101
|
+
|
|
102
|
+
for (const instance of filteredInstances) {
|
|
103
|
+
inputResolverNodes.set(`instance:${instance.id}`, {
|
|
104
|
+
kind: "instance",
|
|
105
|
+
instance,
|
|
106
|
+
component: library.components[instance.type],
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const hub of hubs) {
|
|
111
|
+
inputResolverNodes.set(`hub:${hub.id}`, { kind: "hub", hub })
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const inputResolver = new InputResolver(inputResolverNodes, this.logger)
|
|
115
|
+
inputResolver.addAllNodesToWorkset()
|
|
116
|
+
|
|
117
|
+
const resolvedInputs: Record<string, Record<string, ResolvedInstanceInput[]>> = {}
|
|
118
|
+
|
|
119
|
+
await inputResolver.process()
|
|
120
|
+
|
|
121
|
+
for (const instance of filteredInstances) {
|
|
122
|
+
const output = inputResolver.requireOutput(`instance:${instance.id}`)
|
|
123
|
+
if (output.kind !== "instance") {
|
|
124
|
+
throw new Error("Expected instance node")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
resolvedInputs[instance.id] = output.resolvedInputs
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
project,
|
|
132
|
+
library,
|
|
133
|
+
instances: filteredInstances,
|
|
134
|
+
stateMap,
|
|
135
|
+
resolvedInputs,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
/**
|
|
141
|
+
* Get the appropriate project model backend for the given project.
|
|
142
|
+
*
|
|
143
|
+
* @param type The project storage type.
|
|
144
|
+
* @returns The project model backend.
|
|
145
|
+
*/
|
|
146
|
+
private getProjectModelBackend(type: string): ProjectModelBackend {
|
|
147
|
+
const backend = this.projectModelBackends[type]
|
|
148
|
+
if (!backend) {
|
|
149
|
+
throw new Error(`Project model backend not found for type: ${type}`)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return backend
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get project with model backend and spec.
|
|
157
|
+
*
|
|
158
|
+
* @param projectId The project ID to get.
|
|
159
|
+
* @returns The project, backend, and spec.
|
|
160
|
+
*/
|
|
161
|
+
private async getProjectWithBackend(projectId: string) {
|
|
162
|
+
const project = await this.database.backend.project.findUnique({
|
|
163
|
+
where: { id: projectId },
|
|
164
|
+
select: {
|
|
165
|
+
...forSchema(projectOutputSchema),
|
|
166
|
+
modelStorage: {
|
|
167
|
+
select: {
|
|
168
|
+
spec: true,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
if (!project) {
|
|
175
|
+
throw new ProjectNotFoundError(projectId)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const backend = this.getProjectModelBackend(project.modelStorage.spec.type)
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
project,
|
|
182
|
+
backend,
|
|
183
|
+
spec: project.modelStorage.spec,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Get virtual instances from the database for the given project ID.
|
|
189
|
+
*
|
|
190
|
+
* @param projectId The project ID to get virtual instances for.
|
|
191
|
+
* @returns The list of virtual instances.
|
|
192
|
+
*/
|
|
193
|
+
private async getVirtualInstances(projectId: string): Promise<InstanceModel[]> {
|
|
194
|
+
const database = await this.database.forProject(projectId)
|
|
195
|
+
const states = await database.instanceEvaluationState.findMany({ select: { model: true } })
|
|
196
|
+
|
|
197
|
+
return states.map(state => state.model).filter(isNonNullish)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get ghost instances from the database for the given project ID.
|
|
202
|
+
*
|
|
203
|
+
* @param projectId The project ID to get ghost instances for.
|
|
204
|
+
* @param residentInstanceIds The IDs of the instances present in the model.
|
|
205
|
+
* @return The list of ghost instances.
|
|
206
|
+
*/
|
|
207
|
+
private async getGhostInstances(
|
|
208
|
+
projectId: string,
|
|
209
|
+
residentInstanceIds: string[],
|
|
210
|
+
): Promise<InstanceModel[]> {
|
|
211
|
+
const database = await this.database.forProject(projectId)
|
|
212
|
+
|
|
213
|
+
const states = await database.instanceState.findMany({
|
|
214
|
+
where: {
|
|
215
|
+
// undeployed instances cannot be considered ghost
|
|
216
|
+
status: { not: "undeployed" },
|
|
217
|
+
|
|
218
|
+
OR: [
|
|
219
|
+
// the resident instance is ghost if it is not in the model
|
|
220
|
+
{ source: "resident", instanceId: { notIn: residentInstanceIds } },
|
|
221
|
+
|
|
222
|
+
// the virtual instance is ghost if it is has no evaluation state
|
|
223
|
+
{ source: "virtual", evaluationState: null },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
select: { model: true },
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
return states.map(state => state.model).filter(isNonNullish)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private async syncInstanceStates(projectId: string): Promise<void> {
|
|
233
|
+
const database = await this.database.forProject(projectId)
|
|
234
|
+
|
|
235
|
+
await database.$transaction(async tx => {
|
|
236
|
+
const [{ instances }] = await this.getProjectModel(projectId)
|
|
237
|
+
|
|
238
|
+
const existingStates = await tx.instanceState.findMany({ select: { instanceId: true } })
|
|
239
|
+
const existingStateIds = new Set(existingStates.map(state => state.instanceId))
|
|
240
|
+
const missingInstances = instances.filter(instance => !existingStateIds.has(instance.id))
|
|
241
|
+
|
|
242
|
+
if (missingInstances.length === 0) {
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await tx.instanceState.createMany({
|
|
247
|
+
data: missingInstances.map(instance => ({
|
|
248
|
+
instanceId: instance.id,
|
|
249
|
+
kind: instance.kind,
|
|
250
|
+
source: "resident",
|
|
251
|
+
status: "undeployed",
|
|
252
|
+
})),
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
this.logger.info({ projectId }, "created missing %s instance states", missingInstances.length)
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
}
|