automations backend (#1789)
* feat(server): WIP automations api * feat(server): Automations Backend api WIP take 2 * feat(automations): add validation to automation run schema with zod * fix(server): add zod to package.json * fix(server): automations pr cleanup
This commit is contained in:
@@ -182,6 +182,48 @@ export type AuthStrategy = {
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AutomationFunctionRunStatus = {
|
||||
__typename?: 'AutomationFunctionRunStatus';
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: Maybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AutomationMutations = {
|
||||
__typename?: 'AutomationMutations';
|
||||
create: Scalars['Boolean'];
|
||||
functionRunStatusReport: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsCreateArgs = {
|
||||
input: ModelAutomationCreateInput;
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsFunctionRunStatusReportArgs = {
|
||||
input: ModelAutomationRunStatusUpdateInput;
|
||||
};
|
||||
|
||||
export enum AutomationRunStatus {
|
||||
Failed = 'FAILED',
|
||||
Initializing = 'INITIALIZING',
|
||||
Running = 'RUNNING',
|
||||
Succeeded = 'SUCCEEDED'
|
||||
}
|
||||
|
||||
export type AutomationsStatus = {
|
||||
__typename?: 'AutomationsStatus';
|
||||
automationRuns: Array<ModelAutomationRun>;
|
||||
status: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type BlobMetadata = {
|
||||
__typename?: 'BlobMetadata';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@@ -588,6 +630,17 @@ export type FileUpload = {
|
||||
userId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type FunctionRunStatusInput = {
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: InputMaybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type LegacyCommentViewerData = {
|
||||
__typename?: 'LegacyCommentViewerData';
|
||||
/**
|
||||
@@ -678,6 +731,7 @@ export type LimitedUserTimelineArgs = {
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** Return a model tree of children */
|
||||
childrenTree: Array<ModelsTreeItem>;
|
||||
/** All comment threads in this model */
|
||||
@@ -720,6 +774,54 @@ export type ModelVersionsArgs = {
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomation = {
|
||||
__typename?: 'ModelAutomation';
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
runs: ModelAutomationRunsCollection;
|
||||
};
|
||||
|
||||
|
||||
export type ModelAutomationRunsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomationCreateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
modelId: Scalars['String'];
|
||||
projectId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRun = {
|
||||
__typename?: 'ModelAutomationRun';
|
||||
automationRunId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
functionRunStatuses: Array<AutomationFunctionRunStatus>;
|
||||
runStatus: AutomationRunStatus;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunStatusUpdateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
automationRunId: Scalars['String'];
|
||||
functionRunStatuses: Array<FunctionRunStatusInput>;
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunsCollection = {
|
||||
__typename?: 'ModelAutomationRunsCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items: Array<ModelAutomationRun>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelCollection = {
|
||||
__typename?: 'ModelCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
@@ -805,6 +907,7 @@ export type Mutation = {
|
||||
appRevokeAccess?: Maybe<Scalars['Boolean']>;
|
||||
/** Update an existing third party application. **Note: This will invalidate all existing tokens, refresh tokens and access codes and will require existing users to re-authorize it.** */
|
||||
appUpdate: Scalars['Boolean'];
|
||||
automationMutations: AutomationMutations;
|
||||
branchCreate: Scalars['String'];
|
||||
branchDelete: Scalars['Boolean'];
|
||||
branchUpdate: Scalars['Boolean'];
|
||||
@@ -2554,6 +2657,7 @@ export type UserUpdateInput = {
|
||||
export type Version = {
|
||||
__typename?: 'Version';
|
||||
authorUser?: Maybe<LimitedUser>;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** All comment threads in this version */
|
||||
commentThreads: CommentCollection;
|
||||
createdAt: Scalars['DateTime'];
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
extend type Model {
|
||||
automationStatus: AutomationsStatus
|
||||
}
|
||||
|
||||
type AutomationsStatus {
|
||||
status: AutomationRunStatus!
|
||||
statusMessage: String
|
||||
automationRuns: [ModelAutomationRun!]!
|
||||
}
|
||||
|
||||
extend type Version {
|
||||
automationStatus: AutomationsStatus
|
||||
}
|
||||
|
||||
type ModelAutomation {
|
||||
automationName: String!
|
||||
automationId: String!
|
||||
automationRevisionId: String!
|
||||
createdAt: DateTime!
|
||||
runs(cursor: String, limit: Int! = 25): ModelAutomationRunsCollection!
|
||||
}
|
||||
|
||||
type ModelAutomationRunsCollection {
|
||||
totalCount: Int!
|
||||
cursor: String
|
||||
items: [ModelAutomationRun!]!
|
||||
}
|
||||
|
||||
type ModelAutomationRun {
|
||||
versionId: String!
|
||||
# automation: ModelAutomation!
|
||||
createdAt: DateTime!
|
||||
updatedAt: DateTime!
|
||||
automationRunId: String!
|
||||
functionRunStatuses: [AutomationFunctionRunStatus!]!
|
||||
runStatus: AutomationRunStatus!
|
||||
}
|
||||
|
||||
type AutomationFunctionRunStatus {
|
||||
functionId: String!
|
||||
elapsed: Float!
|
||||
runStatus: AutomationRunStatus!
|
||||
# this is a link to a viewer page with potentially composite
|
||||
contextView: String
|
||||
# TODO: remove this from the schema and add it as a type override
|
||||
resultVersionIds: [String!]!
|
||||
# resultVersions: [Version!]!
|
||||
|
||||
blobs: [String!]!
|
||||
statusMessage: String
|
||||
# its a {version: string; objectResults: Record<string, Array<{level: string; statusMessage: string}>>}
|
||||
# where the record key is the object id
|
||||
objectResults: JSONObject!
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
automationMutations: AutomationMutations!
|
||||
}
|
||||
|
||||
type AutomationMutations {
|
||||
functionRunStatusReport(input: ModelAutomationRunStatusUpdateInput!): Boolean!
|
||||
@hasServerRole(role: SERVER_USER)
|
||||
# @hasStreamRole(role: STREAM_OWNER) # who can do this?
|
||||
# @hasScope(scope: "automation:result") # TODO: add the scope constant
|
||||
create(input: ModelAutomationCreateInput!): Boolean! @hasServerRole(role: SERVER_USER)
|
||||
# @hasStreamRole(role: STREAM_OWNER) # TODO: check for stream ownership in the service
|
||||
# @hasScope(scope: "automation:create") # TODO: add the scope constant
|
||||
}
|
||||
|
||||
input ModelAutomationCreateInput {
|
||||
projectId: String!
|
||||
modelId: String!
|
||||
automationName: String!
|
||||
automationId: String!
|
||||
automationRevisionId: String!
|
||||
}
|
||||
|
||||
input ModelAutomationRunStatusUpdateInput {
|
||||
versionId: String!
|
||||
automationId: String!
|
||||
automationRevisionId: String!
|
||||
automationRunId: String!
|
||||
# functionReleaseId: String!
|
||||
functionRunStatuses: [FunctionRunStatusInput!]!
|
||||
}
|
||||
|
||||
enum AutomationRunStatus {
|
||||
INITIALIZING
|
||||
RUNNING
|
||||
SUCCEEDED
|
||||
FAILED
|
||||
# TIMEOUT needs to be handled as an error
|
||||
}
|
||||
|
||||
input FunctionRunStatusInput {
|
||||
# we cannot strictly require these values, cause local testers of function, wont have it...
|
||||
# Or should we?
|
||||
functionId: String!
|
||||
elapsed: Float!
|
||||
runStatus: AutomationRunStatus!
|
||||
contextView: String
|
||||
resultVersionIds: [String!]!
|
||||
blobs: [String!]!
|
||||
statusMessage: String
|
||||
# its a Record<string, {level: string; statusMessage: string}[]>
|
||||
# where the record key is the object id
|
||||
objectResults: JSONObject!
|
||||
}
|
||||
|
||||
# extend type Subscription {
|
||||
# automationRunStatusUpdated()
|
||||
# }
|
||||
@@ -31,6 +31,8 @@ generates:
|
||||
Comment: '@/modules/comments/helpers/graphTypes#CommentGraphQLReturn'
|
||||
PendingStreamCollaborator: '@/modules/serverinvites/helpers/graphTypes#PendingStreamCollaboratorGraphQLReturn'
|
||||
FileUpload: '@/modules/fileuploads/helpers/types#FileUploadGraphQLReturn'
|
||||
AutomationMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
# ModelAutomationRun: '@/modules/automations/helpers/graphTypes#ModelAutomationRunGraphQLReturn'
|
||||
modules/cross-server-sync/graph/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import {
|
||||
createModelAutomation,
|
||||
getAutomationsStatus,
|
||||
upsertModelAutomationRunResult
|
||||
} from '@/modules/automations/services/automations'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
|
||||
export = {
|
||||
Model: {
|
||||
async automationStatus(parent, _, ctx) {
|
||||
// we're using the branch model name still?
|
||||
const modelId = parent.name
|
||||
const projectId = parent.streamId
|
||||
const latestCommit = await ctx.loaders.branches.getLatestCommit.load(parent.id)
|
||||
// if the model has no versions, no automations could have run
|
||||
if (!latestCommit) return null
|
||||
return await getAutomationsStatus({
|
||||
projectId,
|
||||
modelId,
|
||||
versionId: latestCommit.id
|
||||
})
|
||||
}
|
||||
},
|
||||
Version: {
|
||||
async automationStatus(parent, _, ctx) {
|
||||
const versionId = parent.id
|
||||
const branch = await ctx.loaders.commits.getCommitBranch.load(versionId)
|
||||
if (!branch) throw Error('Very bad version Id')
|
||||
const projectId = branch.streamId
|
||||
// yes, the name, cause of webhooks.
|
||||
const modelId = branch.name
|
||||
return await getAutomationsStatus({
|
||||
projectId,
|
||||
modelId,
|
||||
versionId
|
||||
})
|
||||
}
|
||||
},
|
||||
Mutation: {
|
||||
automationMutations: () => ({})
|
||||
},
|
||||
AutomationMutations: {
|
||||
async create(_, args, context) {
|
||||
await createModelAutomation(args.input, context.userId)
|
||||
return true
|
||||
},
|
||||
async functionRunStatusReport(_, args, context) {
|
||||
const { userId } = context
|
||||
await upsertModelAutomationRunResult({ userId, input: args.input })
|
||||
return true
|
||||
}
|
||||
}
|
||||
} as Resolvers
|
||||
@@ -0,0 +1,54 @@
|
||||
import { AutomationRunStatus } from '@/modules/core/graph/generated/graphql'
|
||||
import { z } from 'zod'
|
||||
|
||||
export type ModelAutomation = {
|
||||
projectId: string
|
||||
modelId: string
|
||||
automationId: string
|
||||
createdAt: Date
|
||||
automationRevisionId: string
|
||||
automationName: string
|
||||
}
|
||||
|
||||
export const SupportedObjectResultsVersions = ['23.09'] as const
|
||||
|
||||
export const ObjectResultLevel = ['INFO', 'WARNING', 'ERROR'] as const
|
||||
|
||||
export const ObjectResultLevelEnum = z.enum(ObjectResultLevel)
|
||||
|
||||
export const ObjectResultValuesSchema = z.object({
|
||||
level: z.enum(['INFO', 'WARNING', 'ERROR']),
|
||||
statusMessage: z.string()
|
||||
})
|
||||
|
||||
export const ObjectResultsSchema = z.object({
|
||||
version: z.enum(SupportedObjectResultsVersions),
|
||||
values: z.record(z.string(), ObjectResultValuesSchema.array())
|
||||
})
|
||||
|
||||
export type ObjectResults = z.infer<typeof ObjectResultsSchema>
|
||||
|
||||
export const FunctionRunStatusSchema = z.object({
|
||||
functionId: z.string().nonempty(),
|
||||
elapsed: z.number(),
|
||||
runStatus: z.nativeEnum(AutomationRunStatus),
|
||||
contextView: z.string().nullable().default(null),
|
||||
resultVersionIds: z.string().array(),
|
||||
blobs: z.string().array(),
|
||||
statusMessage: z.string().nullable().default(null),
|
||||
objectResults: ObjectResultsSchema
|
||||
})
|
||||
|
||||
export type FunctionRunStatus = z.infer<typeof FunctionRunStatusSchema>
|
||||
|
||||
export const AutomationRunSchema = z.object({
|
||||
automationId: z.string().nonempty(),
|
||||
automationRevisionId: z.string().nonempty(),
|
||||
automationRunId: z.string().nonempty(),
|
||||
versionId: z.string().nonempty(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
functionRunStatuses: z.array(FunctionRunStatusSchema)
|
||||
})
|
||||
|
||||
export type AutomationRun = z.infer<typeof AutomationRunSchema>
|
||||
@@ -0,0 +1,10 @@
|
||||
import { moduleLogger } from '@/logging/logging'
|
||||
import { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
|
||||
|
||||
const automationModule: SpeckleModule = {
|
||||
init() {
|
||||
moduleLogger.info('🤖 Init automations module')
|
||||
}
|
||||
}
|
||||
|
||||
export = automationModule
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Knex } from 'knex'
|
||||
|
||||
const AUTOMATIONS_TABLE_NAME = 'automations'
|
||||
const AUTOMATION_RUNS_TABLE_NAME = 'automation_runs'
|
||||
|
||||
const AUTOMATION_ID = 'automationId'
|
||||
const AUTOMATION_REVISION_ID = 'automationRevisionId'
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.createTable(AUTOMATIONS_TABLE_NAME, (table) => {
|
||||
table.string(AUTOMATION_ID).notNullable
|
||||
table.string(AUTOMATION_REVISION_ID).notNullable()
|
||||
table.string('automationName').notNullable()
|
||||
table.string('projectId').notNullable().references('id').inTable('streams')
|
||||
table.string('modelId').notNullable()
|
||||
table
|
||||
.timestamp('createdAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
table.primary([AUTOMATION_ID, AUTOMATION_REVISION_ID])
|
||||
})
|
||||
await knex.schema.createTable(AUTOMATION_RUNS_TABLE_NAME, (table) => {
|
||||
table.string(AUTOMATION_ID).notNullable()
|
||||
table.string(AUTOMATION_REVISION_ID).notNullable()
|
||||
table
|
||||
.string('versionId')
|
||||
.references('id')
|
||||
.inTable('commits')
|
||||
.notNullable()
|
||||
.onDelete('cascade')
|
||||
table.string('automationRunId').primary()
|
||||
table
|
||||
.timestamp('createdAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
table
|
||||
.timestamp('updatedAt', { precision: 3, useTz: true })
|
||||
.defaultTo(knex.fn.now())
|
||||
.notNullable()
|
||||
table.jsonb('data')
|
||||
table
|
||||
.foreign([AUTOMATION_ID, AUTOMATION_REVISION_ID])
|
||||
.references([AUTOMATION_ID, AUTOMATION_REVISION_ID])
|
||||
.inTable(AUTOMATIONS_TABLE_NAME)
|
||||
})
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(AUTOMATIONS_TABLE_NAME)
|
||||
await knex.schema.dropTableIfExists(AUTOMATION_RUNS_TABLE_NAME)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import knex from '@/db/knex'
|
||||
import { ModelAutomation, AutomationRun } from '@/modules/automations/helpers/types'
|
||||
|
||||
const Automations = () => knex('automations')
|
||||
const AutomationRuns = () => knex('automation_runs')
|
||||
|
||||
export const storeModelAutomation = async (automation: ModelAutomation) => {
|
||||
await Automations().insert(automation)
|
||||
}
|
||||
|
||||
export const getModelAutomation = async (
|
||||
automationId: string
|
||||
): Promise<ModelAutomation> => {
|
||||
return await Automations().where({ automationId }).first()
|
||||
}
|
||||
|
||||
export const upsertAutomationRunData = async (automationRun: AutomationRun) => {
|
||||
const insertModel = {
|
||||
automationId: automationRun.automationId,
|
||||
automationRevisionId: automationRun.automationRevisionId,
|
||||
automationRunId: automationRun.automationRunId,
|
||||
versionId: automationRun.versionId,
|
||||
createdAt: automationRun.createdAt,
|
||||
updatedAt: automationRun.updatedAt,
|
||||
data: automationRun
|
||||
}
|
||||
await AutomationRuns().insert(insertModel).onConflict('automationRunId').merge()
|
||||
}
|
||||
|
||||
export const getAutomationRun = async (
|
||||
automationRunId: string
|
||||
): Promise<AutomationRun | null> => {
|
||||
const item = await AutomationRuns().where({ automationRunId }).first()
|
||||
if (!item) return null
|
||||
return item.data
|
||||
}
|
||||
|
||||
export const getLatestAutomationRunsFor = async ({
|
||||
projectId,
|
||||
modelId,
|
||||
versionId
|
||||
}: {
|
||||
projectId: string
|
||||
modelId: string
|
||||
versionId: string
|
||||
}): Promise<AutomationRun[]> => {
|
||||
const runs = await AutomationRuns()
|
||||
.innerJoin(
|
||||
'automations',
|
||||
'automation_runs.automationId',
|
||||
'automations.automationId'
|
||||
)
|
||||
.where({ projectId })
|
||||
.andWhere({ modelId })
|
||||
.andWhere({ versionId })
|
||||
.distinctOn('automation_runs.automationId')
|
||||
.orderBy([
|
||||
{ column: 'automation_runs.automationId' },
|
||||
{ column: 'automation_runs.createdAt', order: 'desc' }
|
||||
])
|
||||
|
||||
return runs.map((r) => r.data)
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import { getStreamBranchByName } from '@/modules/core/repositories/branches'
|
||||
import { getStream } from '@/modules/core/repositories/streams'
|
||||
import { Roles } from '@speckle/shared'
|
||||
import {
|
||||
getAutomationRun,
|
||||
getLatestAutomationRunsFor,
|
||||
getModelAutomation,
|
||||
storeModelAutomation
|
||||
} from '@/modules/automations/repositories/automations'
|
||||
import _ from 'lodash'
|
||||
import {
|
||||
ModelAutomationCreateInput,
|
||||
ModelAutomationRunStatusUpdateInput,
|
||||
AutomationRunStatus,
|
||||
AutomationsStatus
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { upsertAutomationRunData } from '@/modules/automations/repositories/automations'
|
||||
import { AutomationRun, AutomationRunSchema } from '../helpers/types'
|
||||
import { ForbiddenError, BadRequestError } from '@/modules/shared/errors'
|
||||
|
||||
export const createModelAutomation = async (
|
||||
automation: ModelAutomationCreateInput,
|
||||
userId?: string | undefined
|
||||
) => {
|
||||
// stream acl for user
|
||||
const stream = await getStream({ userId, streamId: automation.projectId })
|
||||
if (!stream) throw new BadRequestError('400 invalid projectId')
|
||||
if (stream.role !== Roles.Stream.Owner)
|
||||
throw new ForbiddenError('Only project owners are allowed.')
|
||||
// TODO: once webhooks are migrated to FE2 terms, we need to get the branch model by id
|
||||
// const branch = await getBranchById()
|
||||
const branch = await getStreamBranchByName(automation.projectId, automation.modelId)
|
||||
if (!branch) throw new BadRequestError('400 invalid modelId')
|
||||
const insertModel = { ...automation, createdAt: new Date() }
|
||||
await storeModelAutomation(insertModel)
|
||||
}
|
||||
|
||||
export async function upsertModelAutomationRunResult({
|
||||
userId,
|
||||
input
|
||||
}: {
|
||||
userId: string | null | undefined
|
||||
input: ModelAutomationRunStatusUpdateInput
|
||||
}) {
|
||||
// 1. get the automation from the DB
|
||||
const automation = await getModelAutomation(input.automationId)
|
||||
// 2. authz the current user on the automation
|
||||
const stream = await getStream({
|
||||
userId: userId || undefined,
|
||||
streamId: automation.projectId
|
||||
})
|
||||
if (!stream) throw new BadRequestError('400 invalid projectId')
|
||||
if (stream.role !== Roles.Stream.Owner)
|
||||
throw new ForbiddenError('Only project owners are allowed')
|
||||
// 3. store the result of the run, if it already exists, patch it
|
||||
const maybeAutomationRun = await getAutomationRun(input.automationRunId)
|
||||
const insertModel = AutomationRunSchema.parse({
|
||||
...input,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
})
|
||||
|
||||
if (maybeAutomationRun) {
|
||||
// some bits we do not allow overriding
|
||||
insertModel.createdAt = maybeAutomationRun.createdAt
|
||||
insertModel.versionId = maybeAutomationRun.versionId
|
||||
insertModel.automationId = maybeAutomationRun.automationId
|
||||
insertModel.automationRevisionId = maybeAutomationRun.automationRevisionId
|
||||
|
||||
// if the function run status is not in the update, add it from the DB to not loose its data
|
||||
maybeAutomationRun.functionRunStatuses.map((functionRunStatus) => {
|
||||
if (
|
||||
!insertModel.functionRunStatuses.some(
|
||||
(frs) => frs.functionId === functionRunStatus.functionId
|
||||
)
|
||||
)
|
||||
insertModel.functionRunStatuses.push(functionRunStatus)
|
||||
})
|
||||
}
|
||||
await upsertAutomationRunData(insertModel)
|
||||
// 4. publish an event for new automation run creation
|
||||
// 5. publish an event for new run result update
|
||||
// the last two events should be separated.
|
||||
// automate should publish the new run linked to the model / version, with function run results as pending
|
||||
// the running function should only update the function run result
|
||||
// this is, so that FE subscriptions can be added properly.
|
||||
// when a new run is triggered, the frontend should react to that
|
||||
// also for the result of independent function run results.
|
||||
// we're now shortcutting this until the one automation one function barrier is there.
|
||||
// publish(automationRunStatusUpdate, { foo: 'bar' })
|
||||
}
|
||||
|
||||
const anyFunctionRunsHaveStatus = (ar: AutomationRun, status: AutomationRunStatus) =>
|
||||
ar.functionRunStatuses.some((st) => st.runStatus === status)
|
||||
|
||||
const anyFunctionRunsHaveFailed = (ar: AutomationRun): boolean =>
|
||||
anyFunctionRunsHaveStatus(ar, AutomationRunStatus.Failed)
|
||||
|
||||
const anyFunctionRunsRunning = (ar: AutomationRun): boolean =>
|
||||
anyFunctionRunsHaveStatus(ar, AutomationRunStatus.Running)
|
||||
|
||||
const anyFunctionRunsInitializing = (ar: AutomationRun): boolean =>
|
||||
anyFunctionRunsHaveStatus(ar, AutomationRunStatus.Initializing)
|
||||
|
||||
export const getAutomationsStatus = async ({
|
||||
projectId,
|
||||
modelId,
|
||||
versionId
|
||||
}: {
|
||||
projectId: string
|
||||
modelId: string
|
||||
versionId: string
|
||||
}): Promise<AutomationsStatus | null> => {
|
||||
const automationRuns = await getLatestAutomationRunsFor({
|
||||
projectId,
|
||||
modelId,
|
||||
versionId
|
||||
})
|
||||
// automation is registered, but no run status have been reported
|
||||
if (!automationRuns.length) return null
|
||||
|
||||
const modelAutomationRuns = automationRuns.map((ar) => {
|
||||
let status: AutomationRunStatus = AutomationRunStatus.Succeeded
|
||||
if (anyFunctionRunsHaveFailed(ar)) {
|
||||
status = AutomationRunStatus.Failed
|
||||
} else if (anyFunctionRunsRunning(ar)) {
|
||||
status = AutomationRunStatus.Running
|
||||
} else if (anyFunctionRunsInitializing(ar)) {
|
||||
status = AutomationRunStatus.Initializing
|
||||
}
|
||||
return { ..._.cloneDeep(ar), runStatus: status }
|
||||
})
|
||||
|
||||
const failedAutomations = modelAutomationRuns.filter(
|
||||
(a) => a.runStatus === AutomationRunStatus.Failed
|
||||
)
|
||||
|
||||
const runningAutomations = modelAutomationRuns.filter(
|
||||
(a) => a.runStatus === AutomationRunStatus.Running
|
||||
)
|
||||
const initializingAutomations = modelAutomationRuns.filter(
|
||||
(a) => a.runStatus === AutomationRunStatus.Initializing
|
||||
)
|
||||
|
||||
let status = AutomationRunStatus.Succeeded
|
||||
let statusMessage = 'All automations have succeeded'
|
||||
|
||||
if (failedAutomations.length) {
|
||||
status = AutomationRunStatus.Failed
|
||||
statusMessage = 'Some automations have failed:'
|
||||
for (const fa of failedAutomations) {
|
||||
for (const functionRunStatus of fa.functionRunStatuses) {
|
||||
if (functionRunStatus.runStatus === AutomationRunStatus.Failed)
|
||||
statusMessage += `\n${functionRunStatus.statusMessage}`
|
||||
}
|
||||
}
|
||||
} else if (runningAutomations.length) {
|
||||
status = AutomationRunStatus.Running
|
||||
} else if (initializingAutomations.length) {
|
||||
status = AutomationRunStatus.Initializing
|
||||
}
|
||||
return {
|
||||
status: status as AutomationRunStatus,
|
||||
automationRuns: modelAutomationRuns,
|
||||
statusMessage
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,48 @@ export type AuthStrategy = {
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AutomationFunctionRunStatus = {
|
||||
__typename?: 'AutomationFunctionRunStatus';
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: Maybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AutomationMutations = {
|
||||
__typename?: 'AutomationMutations';
|
||||
create: Scalars['Boolean'];
|
||||
functionRunStatusReport: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsCreateArgs = {
|
||||
input: ModelAutomationCreateInput;
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsFunctionRunStatusReportArgs = {
|
||||
input: ModelAutomationRunStatusUpdateInput;
|
||||
};
|
||||
|
||||
export enum AutomationRunStatus {
|
||||
Failed = 'FAILED',
|
||||
Initializing = 'INITIALIZING',
|
||||
Running = 'RUNNING',
|
||||
Succeeded = 'SUCCEEDED'
|
||||
}
|
||||
|
||||
export type AutomationsStatus = {
|
||||
__typename?: 'AutomationsStatus';
|
||||
automationRuns: Array<ModelAutomationRun>;
|
||||
status: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type BlobMetadata = {
|
||||
__typename?: 'BlobMetadata';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@@ -600,6 +642,17 @@ export type FileUpload = {
|
||||
userId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type FunctionRunStatusInput = {
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: InputMaybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type LegacyCommentViewerData = {
|
||||
__typename?: 'LegacyCommentViewerData';
|
||||
/**
|
||||
@@ -690,6 +743,7 @@ export type LimitedUserTimelineArgs = {
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** Return a model tree of children */
|
||||
childrenTree: Array<ModelsTreeItem>;
|
||||
/** All comment threads in this model */
|
||||
@@ -732,6 +786,54 @@ export type ModelVersionsArgs = {
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomation = {
|
||||
__typename?: 'ModelAutomation';
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
runs: ModelAutomationRunsCollection;
|
||||
};
|
||||
|
||||
|
||||
export type ModelAutomationRunsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomationCreateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
modelId: Scalars['String'];
|
||||
projectId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRun = {
|
||||
__typename?: 'ModelAutomationRun';
|
||||
automationRunId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
functionRunStatuses: Array<AutomationFunctionRunStatus>;
|
||||
runStatus: AutomationRunStatus;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunStatusUpdateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
automationRunId: Scalars['String'];
|
||||
functionRunStatuses: Array<FunctionRunStatusInput>;
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunsCollection = {
|
||||
__typename?: 'ModelAutomationRunsCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items: Array<ModelAutomationRun>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelCollection = {
|
||||
__typename?: 'ModelCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
@@ -817,6 +919,7 @@ export type Mutation = {
|
||||
appRevokeAccess?: Maybe<Scalars['Boolean']>;
|
||||
/** Update an existing third party application. **Note: This will invalidate all existing tokens, refresh tokens and access codes and will require existing users to re-authorize it.** */
|
||||
appUpdate: Scalars['Boolean'];
|
||||
automationMutations: AutomationMutations;
|
||||
branchCreate: Scalars['String'];
|
||||
branchDelete: Scalars['Boolean'];
|
||||
branchUpdate: Scalars['Boolean'];
|
||||
@@ -2566,6 +2669,7 @@ export type UserUpdateInput = {
|
||||
export type Version = {
|
||||
__typename?: 'Version';
|
||||
authorUser?: Maybe<LimitedUser>;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** All comment threads in this version */
|
||||
commentThreads: CommentCollection;
|
||||
createdAt: Scalars['DateTime'];
|
||||
@@ -2816,6 +2920,10 @@ export type ResolversTypes = {
|
||||
AppCreateInput: AppCreateInput;
|
||||
AppUpdateInput: AppUpdateInput;
|
||||
AuthStrategy: ResolverTypeWrapper<AuthStrategy>;
|
||||
AutomationFunctionRunStatus: ResolverTypeWrapper<AutomationFunctionRunStatus>;
|
||||
AutomationMutations: ResolverTypeWrapper<MutationsObjectGraphQLReturn>;
|
||||
AutomationRunStatus: AutomationRunStatus;
|
||||
AutomationsStatus: ResolverTypeWrapper<AutomationsStatus>;
|
||||
BigInt: ResolverTypeWrapper<Scalars['BigInt']>;
|
||||
BlobMetadata: ResolverTypeWrapper<BlobMetadata>;
|
||||
BlobMetadataCollection: ResolverTypeWrapper<BlobMetadataCollection>;
|
||||
@@ -2856,12 +2964,18 @@ export type ResolversTypes = {
|
||||
EmailAddress: ResolverTypeWrapper<Scalars['EmailAddress']>;
|
||||
FileUpload: ResolverTypeWrapper<FileUploadGraphQLReturn>;
|
||||
Float: ResolverTypeWrapper<Scalars['Float']>;
|
||||
FunctionRunStatusInput: FunctionRunStatusInput;
|
||||
ID: ResolverTypeWrapper<Scalars['ID']>;
|
||||
Int: ResolverTypeWrapper<Scalars['Int']>;
|
||||
JSONObject: ResolverTypeWrapper<Scalars['JSONObject']>;
|
||||
LegacyCommentViewerData: ResolverTypeWrapper<LegacyCommentViewerData>;
|
||||
LimitedUser: ResolverTypeWrapper<LimitedUserGraphQLReturn>;
|
||||
Model: ResolverTypeWrapper<ModelGraphQLReturn>;
|
||||
ModelAutomation: ResolverTypeWrapper<ModelAutomation>;
|
||||
ModelAutomationCreateInput: ModelAutomationCreateInput;
|
||||
ModelAutomationRun: ResolverTypeWrapper<ModelAutomationRun>;
|
||||
ModelAutomationRunStatusUpdateInput: ModelAutomationRunStatusUpdateInput;
|
||||
ModelAutomationRunsCollection: ResolverTypeWrapper<ModelAutomationRunsCollection>;
|
||||
ModelCollection: ResolverTypeWrapper<Omit<ModelCollection, 'items'> & { items: Array<ResolversTypes['Model']> }>;
|
||||
ModelMutations: ResolverTypeWrapper<MutationsObjectGraphQLReturn>;
|
||||
ModelVersionsFilter: ModelVersionsFilter;
|
||||
@@ -2980,6 +3094,9 @@ export type ResolversParentTypes = {
|
||||
AppCreateInput: AppCreateInput;
|
||||
AppUpdateInput: AppUpdateInput;
|
||||
AuthStrategy: AuthStrategy;
|
||||
AutomationFunctionRunStatus: AutomationFunctionRunStatus;
|
||||
AutomationMutations: MutationsObjectGraphQLReturn;
|
||||
AutomationsStatus: AutomationsStatus;
|
||||
BigInt: Scalars['BigInt'];
|
||||
BlobMetadata: BlobMetadata;
|
||||
BlobMetadataCollection: BlobMetadataCollection;
|
||||
@@ -3019,12 +3136,18 @@ export type ResolversParentTypes = {
|
||||
EmailAddress: Scalars['EmailAddress'];
|
||||
FileUpload: FileUploadGraphQLReturn;
|
||||
Float: Scalars['Float'];
|
||||
FunctionRunStatusInput: FunctionRunStatusInput;
|
||||
ID: Scalars['ID'];
|
||||
Int: Scalars['Int'];
|
||||
JSONObject: Scalars['JSONObject'];
|
||||
LegacyCommentViewerData: LegacyCommentViewerData;
|
||||
LimitedUser: LimitedUserGraphQLReturn;
|
||||
Model: ModelGraphQLReturn;
|
||||
ModelAutomation: ModelAutomation;
|
||||
ModelAutomationCreateInput: ModelAutomationCreateInput;
|
||||
ModelAutomationRun: ModelAutomationRun;
|
||||
ModelAutomationRunStatusUpdateInput: ModelAutomationRunStatusUpdateInput;
|
||||
ModelAutomationRunsCollection: ModelAutomationRunsCollection;
|
||||
ModelCollection: Omit<ModelCollection, 'items'> & { items: Array<ResolversParentTypes['Model']> };
|
||||
ModelMutations: MutationsObjectGraphQLReturn;
|
||||
ModelVersionsFilter: ModelVersionsFilter;
|
||||
@@ -3240,6 +3363,31 @@ export type AuthStrategyResolvers<ContextType = GraphQLContext, ParentType exten
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type AutomationFunctionRunStatusResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['AutomationFunctionRunStatus'] = ResolversParentTypes['AutomationFunctionRunStatus']> = {
|
||||
blobs?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
contextView?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
elapsed?: Resolver<ResolversTypes['Float'], ParentType, ContextType>;
|
||||
functionId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
objectResults?: Resolver<ResolversTypes['JSONObject'], ParentType, ContextType>;
|
||||
resultVersionIds?: Resolver<Array<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
runStatus?: Resolver<ResolversTypes['AutomationRunStatus'], ParentType, ContextType>;
|
||||
statusMessage?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type AutomationMutationsResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['AutomationMutations'] = ResolversParentTypes['AutomationMutations']> = {
|
||||
create?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<AutomationMutationsCreateArgs, 'input'>>;
|
||||
functionRunStatusReport?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<AutomationMutationsFunctionRunStatusReportArgs, 'input'>>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type AutomationsStatusResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['AutomationsStatus'] = ResolversParentTypes['AutomationsStatus']> = {
|
||||
automationRuns?: Resolver<Array<ResolversTypes['ModelAutomationRun']>, ParentType, ContextType>;
|
||||
status?: Resolver<ResolversTypes['AutomationRunStatus'], ParentType, ContextType>;
|
||||
statusMessage?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export interface BigIntScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['BigInt'], any> {
|
||||
name: 'BigInt';
|
||||
}
|
||||
@@ -3440,6 +3588,7 @@ export type LimitedUserResolvers<ContextType = GraphQLContext, ParentType extend
|
||||
|
||||
export type ModelResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['Model'] = ResolversParentTypes['Model']> = {
|
||||
author?: Resolver<ResolversTypes['LimitedUser'], ParentType, ContextType>;
|
||||
automationStatus?: Resolver<Maybe<ResolversTypes['AutomationsStatus']>, ParentType, ContextType>;
|
||||
childrenTree?: Resolver<Array<ResolversTypes['ModelsTreeItem']>, ParentType, ContextType>;
|
||||
commentThreads?: Resolver<ResolversTypes['CommentCollection'], ParentType, ContextType, RequireFields<ModelCommentThreadsArgs, 'limit'>>;
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
@@ -3455,6 +3604,32 @@ export type ModelResolvers<ContextType = GraphQLContext, ParentType extends Reso
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ModelAutomationResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ModelAutomation'] = ResolversParentTypes['ModelAutomation']> = {
|
||||
automationId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
automationName?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
automationRevisionId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
runs?: Resolver<ResolversTypes['ModelAutomationRunsCollection'], ParentType, ContextType, RequireFields<ModelAutomationRunsArgs, 'limit'>>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ModelAutomationRunResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ModelAutomationRun'] = ResolversParentTypes['ModelAutomationRun']> = {
|
||||
automationRunId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
functionRunStatuses?: Resolver<Array<ResolversTypes['AutomationFunctionRunStatus']>, ParentType, ContextType>;
|
||||
runStatus?: Resolver<ResolversTypes['AutomationRunStatus'], ParentType, ContextType>;
|
||||
updatedAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
versionId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ModelAutomationRunsCollectionResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ModelAutomationRunsCollection'] = ResolversParentTypes['ModelAutomationRunsCollection']> = {
|
||||
cursor?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
items?: Resolver<Array<ResolversTypes['ModelAutomationRun']>, ParentType, ContextType>;
|
||||
totalCount?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ModelCollectionResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ModelCollection'] = ResolversParentTypes['ModelCollection']> = {
|
||||
cursor?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
items?: Resolver<Array<ResolversTypes['Model']>, ParentType, ContextType>;
|
||||
@@ -3497,6 +3672,7 @@ export type MutationResolvers<ContextType = GraphQLContext, ParentType extends R
|
||||
appDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationAppDeleteArgs, 'appId'>>;
|
||||
appRevokeAccess?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType, RequireFields<MutationAppRevokeAccessArgs, 'appId'>>;
|
||||
appUpdate?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationAppUpdateArgs, 'app'>>;
|
||||
automationMutations?: Resolver<ResolversTypes['AutomationMutations'], ParentType, ContextType>;
|
||||
branchCreate?: Resolver<ResolversTypes['String'], ParentType, ContextType, RequireFields<MutationBranchCreateArgs, 'branch'>>;
|
||||
branchDelete?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationBranchDeleteArgs, 'branch'>>;
|
||||
branchUpdate?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType, RequireFields<MutationBranchUpdateArgs, 'branch'>>;
|
||||
@@ -3974,6 +4150,7 @@ export type UserSearchResultCollectionResolvers<ContextType = GraphQLContext, Pa
|
||||
|
||||
export type VersionResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['Version'] = ResolversParentTypes['Version']> = {
|
||||
authorUser?: Resolver<Maybe<ResolversTypes['LimitedUser']>, ParentType, ContextType>;
|
||||
automationStatus?: Resolver<Maybe<ResolversTypes['AutomationsStatus']>, ParentType, ContextType>;
|
||||
commentThreads?: Resolver<ResolversTypes['CommentCollection'], ParentType, ContextType, RequireFields<VersionCommentThreadsArgs, 'limit'>>;
|
||||
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
@@ -4069,6 +4246,9 @@ export type Resolvers<ContextType = GraphQLContext> = {
|
||||
ApiToken?: ApiTokenResolvers<ContextType>;
|
||||
AppAuthor?: AppAuthorResolvers<ContextType>;
|
||||
AuthStrategy?: AuthStrategyResolvers<ContextType>;
|
||||
AutomationFunctionRunStatus?: AutomationFunctionRunStatusResolvers<ContextType>;
|
||||
AutomationMutations?: AutomationMutationsResolvers<ContextType>;
|
||||
AutomationsStatus?: AutomationsStatusResolvers<ContextType>;
|
||||
BigInt?: GraphQLScalarType;
|
||||
BlobMetadata?: BlobMetadataResolvers<ContextType>;
|
||||
BlobMetadataCollection?: BlobMetadataCollectionResolvers<ContextType>;
|
||||
@@ -4090,6 +4270,9 @@ export type Resolvers<ContextType = GraphQLContext> = {
|
||||
LegacyCommentViewerData?: LegacyCommentViewerDataResolvers<ContextType>;
|
||||
LimitedUser?: LimitedUserResolvers<ContextType>;
|
||||
Model?: ModelResolvers<ContextType>;
|
||||
ModelAutomation?: ModelAutomationResolvers<ContextType>;
|
||||
ModelAutomationRun?: ModelAutomationRunResolvers<ContextType>;
|
||||
ModelAutomationRunsCollection?: ModelAutomationRunsCollectionResolvers<ContextType>;
|
||||
ModelCollection?: ModelCollectionResolvers<ContextType>;
|
||||
ModelMutations?: ModelMutationsResolvers<ContextType>;
|
||||
ModelsTreeItem?: ModelsTreeItemResolvers<ContextType>;
|
||||
|
||||
@@ -180,6 +180,48 @@ export type AuthStrategy = {
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AutomationFunctionRunStatus = {
|
||||
__typename?: 'AutomationFunctionRunStatus';
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: Maybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AutomationMutations = {
|
||||
__typename?: 'AutomationMutations';
|
||||
create: Scalars['Boolean'];
|
||||
functionRunStatusReport: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsCreateArgs = {
|
||||
input: ModelAutomationCreateInput;
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsFunctionRunStatusReportArgs = {
|
||||
input: ModelAutomationRunStatusUpdateInput;
|
||||
};
|
||||
|
||||
export enum AutomationRunStatus {
|
||||
Failed = 'FAILED',
|
||||
Initializing = 'INITIALIZING',
|
||||
Running = 'RUNNING',
|
||||
Succeeded = 'SUCCEEDED'
|
||||
}
|
||||
|
||||
export type AutomationsStatus = {
|
||||
__typename?: 'AutomationsStatus';
|
||||
automationRuns: Array<ModelAutomationRun>;
|
||||
status: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type BlobMetadata = {
|
||||
__typename?: 'BlobMetadata';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@@ -591,6 +633,17 @@ export type FileUpload = {
|
||||
userId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type FunctionRunStatusInput = {
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: InputMaybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type LegacyCommentViewerData = {
|
||||
__typename?: 'LegacyCommentViewerData';
|
||||
/**
|
||||
@@ -681,6 +734,7 @@ export type LimitedUserTimelineArgs = {
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** Return a model tree of children */
|
||||
childrenTree: Array<ModelsTreeItem>;
|
||||
/** All comment threads in this model */
|
||||
@@ -723,6 +777,54 @@ export type ModelVersionsArgs = {
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomation = {
|
||||
__typename?: 'ModelAutomation';
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
runs: ModelAutomationRunsCollection;
|
||||
};
|
||||
|
||||
|
||||
export type ModelAutomationRunsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomationCreateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
modelId: Scalars['String'];
|
||||
projectId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRun = {
|
||||
__typename?: 'ModelAutomationRun';
|
||||
automationRunId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
functionRunStatuses: Array<AutomationFunctionRunStatus>;
|
||||
runStatus: AutomationRunStatus;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunStatusUpdateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
automationRunId: Scalars['String'];
|
||||
functionRunStatuses: Array<FunctionRunStatusInput>;
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunsCollection = {
|
||||
__typename?: 'ModelAutomationRunsCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items: Array<ModelAutomationRun>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelCollection = {
|
||||
__typename?: 'ModelCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
@@ -808,6 +910,7 @@ export type Mutation = {
|
||||
appRevokeAccess?: Maybe<Scalars['Boolean']>;
|
||||
/** Update an existing third party application. **Note: This will invalidate all existing tokens, refresh tokens and access codes and will require existing users to re-authorize it.** */
|
||||
appUpdate: Scalars['Boolean'];
|
||||
automationMutations: AutomationMutations;
|
||||
branchCreate: Scalars['String'];
|
||||
branchDelete: Scalars['Boolean'];
|
||||
branchUpdate: Scalars['Boolean'];
|
||||
@@ -2557,6 +2660,7 @@ export type UserUpdateInput = {
|
||||
export type Version = {
|
||||
__typename?: 'Version';
|
||||
authorUser?: Maybe<LimitedUser>;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** All comment threads in this version */
|
||||
commentThreads: CommentCollection;
|
||||
createdAt: Scalars['DateTime'];
|
||||
|
||||
@@ -57,7 +57,8 @@ async function getSpeckleModules() {
|
||||
'./activitystream',
|
||||
'./accessrequests',
|
||||
'./webhooks',
|
||||
'./cross-server-sync'
|
||||
'./cross-server-sync',
|
||||
'./automations'
|
||||
]
|
||||
|
||||
for (const dir of moduleDirs) {
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
"undici": "^5.19.1",
|
||||
"verror": "^1.10.1",
|
||||
"xml-escape": "^1.1.0",
|
||||
"zod": "^3.22.2",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -180,6 +180,48 @@ export type AuthStrategy = {
|
||||
url: Scalars['String'];
|
||||
};
|
||||
|
||||
export type AutomationFunctionRunStatus = {
|
||||
__typename?: 'AutomationFunctionRunStatus';
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: Maybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type AutomationMutations = {
|
||||
__typename?: 'AutomationMutations';
|
||||
create: Scalars['Boolean'];
|
||||
functionRunStatusReport: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsCreateArgs = {
|
||||
input: ModelAutomationCreateInput;
|
||||
};
|
||||
|
||||
|
||||
export type AutomationMutationsFunctionRunStatusReportArgs = {
|
||||
input: ModelAutomationRunStatusUpdateInput;
|
||||
};
|
||||
|
||||
export enum AutomationRunStatus {
|
||||
Failed = 'FAILED',
|
||||
Initializing = 'INITIALIZING',
|
||||
Running = 'RUNNING',
|
||||
Succeeded = 'SUCCEEDED'
|
||||
}
|
||||
|
||||
export type AutomationsStatus = {
|
||||
__typename?: 'AutomationsStatus';
|
||||
automationRuns: Array<ModelAutomationRun>;
|
||||
status: AutomationRunStatus;
|
||||
statusMessage?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type BlobMetadata = {
|
||||
__typename?: 'BlobMetadata';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@@ -591,6 +633,17 @@ export type FileUpload = {
|
||||
userId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type FunctionRunStatusInput = {
|
||||
blobs: Array<Scalars['String']>;
|
||||
contextView?: InputMaybe<Scalars['String']>;
|
||||
elapsed: Scalars['Float'];
|
||||
functionId: Scalars['String'];
|
||||
objectResults: Scalars['JSONObject'];
|
||||
resultVersionIds: Array<Scalars['String']>;
|
||||
runStatus: AutomationRunStatus;
|
||||
statusMessage?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type LegacyCommentViewerData = {
|
||||
__typename?: 'LegacyCommentViewerData';
|
||||
/**
|
||||
@@ -681,6 +734,7 @@ export type LimitedUserTimelineArgs = {
|
||||
export type Model = {
|
||||
__typename?: 'Model';
|
||||
author: LimitedUser;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** Return a model tree of children */
|
||||
childrenTree: Array<ModelsTreeItem>;
|
||||
/** All comment threads in this model */
|
||||
@@ -723,6 +777,54 @@ export type ModelVersionsArgs = {
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomation = {
|
||||
__typename?: 'ModelAutomation';
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
runs: ModelAutomationRunsCollection;
|
||||
};
|
||||
|
||||
|
||||
export type ModelAutomationRunsArgs = {
|
||||
cursor?: InputMaybe<Scalars['String']>;
|
||||
limit?: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelAutomationCreateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationName: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
modelId: Scalars['String'];
|
||||
projectId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRun = {
|
||||
__typename?: 'ModelAutomationRun';
|
||||
automationRunId: Scalars['String'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
functionRunStatuses: Array<AutomationFunctionRunStatus>;
|
||||
runStatus: AutomationRunStatus;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunStatusUpdateInput = {
|
||||
automationId: Scalars['String'];
|
||||
automationRevisionId: Scalars['String'];
|
||||
automationRunId: Scalars['String'];
|
||||
functionRunStatuses: Array<FunctionRunStatusInput>;
|
||||
versionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ModelAutomationRunsCollection = {
|
||||
__typename?: 'ModelAutomationRunsCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
items: Array<ModelAutomationRun>;
|
||||
totalCount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type ModelCollection = {
|
||||
__typename?: 'ModelCollection';
|
||||
cursor?: Maybe<Scalars['String']>;
|
||||
@@ -808,6 +910,7 @@ export type Mutation = {
|
||||
appRevokeAccess?: Maybe<Scalars['Boolean']>;
|
||||
/** Update an existing third party application. **Note: This will invalidate all existing tokens, refresh tokens and access codes and will require existing users to re-authorize it.** */
|
||||
appUpdate: Scalars['Boolean'];
|
||||
automationMutations: AutomationMutations;
|
||||
branchCreate: Scalars['String'];
|
||||
branchDelete: Scalars['Boolean'];
|
||||
branchUpdate: Scalars['Boolean'];
|
||||
@@ -2557,6 +2660,7 @@ export type UserUpdateInput = {
|
||||
export type Version = {
|
||||
__typename?: 'Version';
|
||||
authorUser?: Maybe<LimitedUser>;
|
||||
automationStatus?: Maybe<AutomationsStatus>;
|
||||
/** All comment threads in this version */
|
||||
commentThreads: CommentCollection;
|
||||
createdAt: Scalars['DateTime'];
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
},
|
||||
"files.eol": "\n",
|
||||
"volar.vueserver.maxOldSpaceSize": 4000,
|
||||
"cSpell.words": ["Bursty", "mjml"],
|
||||
"cSpell.words": ["Automations", "Bursty", "mjml"],
|
||||
"tailwindCSS.experimental.configFile": {
|
||||
"packages/frontend-2/tailwind.config.mjs": "packages/frontend-2/**"
|
||||
},
|
||||
@@ -104,7 +104,7 @@
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
}
|
||||
},
|
||||
"extensions": {
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
|
||||
@@ -12791,6 +12791,7 @@ __metadata:
|
||||
ws: ^7.5.7
|
||||
xml-escape: ^1.1.0
|
||||
yargs: ^17.3.1
|
||||
zod: ^3.22.2
|
||||
zxcvbn: ^4.4.2
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
@@ -44173,6 +44174,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.22.2":
|
||||
version: 3.22.2
|
||||
resolution: "zod@npm:3.22.2"
|
||||
checksum: 231e2180c8eabb56e88680d80baff5cf6cbe6d64df3c44c50ebe52f73081ecd0229b1c7215b9552537f537a36d9e36afac2737ddd86dc14e3519bdbc777e82b9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zxcvbn@npm:^4.4.2":
|
||||
version: 4.4.2
|
||||
resolution: "zxcvbn@npm:4.4.2"
|
||||
|
||||
Reference in New Issue
Block a user