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:
Gergő Jedlicska
2023-09-11 14:38:15 +02:00
committed by GitHub
parent dd20c22a63
commit 8063652dbf
17 changed files with 1020 additions and 3 deletions
@@ -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()
# }
+2
View File
@@ -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'];
+2 -1
View File
@@ -57,7 +57,8 @@ async function getSpeckleModules() {
'./activitystream',
'./accessrequests',
'./webhooks',
'./cross-server-sync'
'./cross-server-sync',
'./automations'
]
for (const dir of moduleDirs) {
+1
View File
@@ -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'];
+2 -2
View File
@@ -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.
+8
View File
@@ -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"