bde148f286
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
433 lines
15 KiB
TypeScript
433 lines
15 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import {
|
|
AutomationNotFoundError,
|
|
FunctionNotFoundError
|
|
} from '@/modules/automate/errors/management'
|
|
import { functionTemplateRepos } from '@/modules/automate/helpers/executionEngine'
|
|
import {
|
|
AutomationRevisionTriggerDefinitionGraphQLReturn,
|
|
AutomationRunTriggerGraphQLReturn
|
|
} from '@/modules/automate/helpers/graphTypes'
|
|
import { VersionCreationTriggerType } from '@/modules/automate/helpers/types'
|
|
import { BranchCommits, Branches, Commits } from '@/modules/core/dbSchema'
|
|
import { AutomateRunStatus } from '@/modules/core/graph/generated/graphql'
|
|
import { SpeckleModuleMocksConfig } from '@/modules/shared/helpers/mocks'
|
|
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
|
|
import { faker } from '@faker-js/faker'
|
|
import { Automate, isNullOrUndefined, SourceAppNames } from '@speckle/shared'
|
|
import dayjs from 'dayjs'
|
|
import { times } from 'lodash-es'
|
|
|
|
const { FF_AUTOMATE_MODULE_ENABLED } = getFeatureFlags()
|
|
|
|
const getRandomModelVersion = async (offset?: number) => {
|
|
const versionQ = Commits.knex()
|
|
.join(BranchCommits.name, BranchCommits.col.commitId, Commits.col.id)
|
|
.first()
|
|
if (offset) versionQ.offset(offset)
|
|
const version = await versionQ
|
|
|
|
if (!version) {
|
|
throw new Error("Couldn't find even one commit in the DB, please create some")
|
|
}
|
|
|
|
const model = await Branches.knex()
|
|
.join(BranchCommits.name, BranchCommits.col.branchId, Branches.col.id)
|
|
.where(BranchCommits.col.commitId, version.id)
|
|
.first()
|
|
|
|
if (!model) {
|
|
throw new Error(
|
|
`Couldn't find branch for first commit #${version.id}, please create one `
|
|
)
|
|
}
|
|
|
|
return {
|
|
model,
|
|
version
|
|
}
|
|
}
|
|
|
|
const mocks: SpeckleModuleMocksConfig = FF_AUTOMATE_MODULE_ENABLED
|
|
? {
|
|
resolvers: ({ store, helpers: { getMockRef, resolveAndCache } }) => ({
|
|
AutomationRevisionTriggerDefinition: {
|
|
__resolveType: () => 'VersionCreatedTriggerDefinition'
|
|
},
|
|
AutomationRunTrigger: {
|
|
__resolveType: () => 'VersionCreatedTrigger'
|
|
},
|
|
VersionCreatedTriggerDefinition: {
|
|
model: store.get('Model') as any
|
|
},
|
|
VersionCreatedTrigger: {
|
|
model: store.get('Model') as any,
|
|
version: store.get('Version') as any
|
|
},
|
|
Query: {
|
|
automateFunctions: () => {
|
|
const forceZero = false
|
|
const count = forceZero ? 0 : faker.number.int({ min: 0, max: 20 })
|
|
|
|
return {
|
|
cursor: null,
|
|
totalCount: count,
|
|
items: times(count, () => store.get('AutomateFunction'))
|
|
} as any
|
|
},
|
|
automateFunction: (_parent, args) => {
|
|
const id = args.id
|
|
if (id === '404') {
|
|
throw new FunctionNotFoundError()
|
|
}
|
|
|
|
return store.get('AutomateFunction', { id }) as any
|
|
}
|
|
},
|
|
User: {
|
|
automateFunctions: () => {
|
|
const count = faker.number.int({ min: 0, max: 20 })
|
|
|
|
return {
|
|
cursor: null,
|
|
totalCount: count,
|
|
items: times(count, () => store.get('AutomateFunction'))
|
|
} as any
|
|
}
|
|
},
|
|
Workspace: {
|
|
automateFunctions: () => {
|
|
const count = faker.number.int({ min: 0, max: 20 })
|
|
|
|
return {
|
|
cursor: null,
|
|
totalCount: count,
|
|
items: times(count, () => store.get('AutomateFunction'))
|
|
} as any
|
|
}
|
|
},
|
|
Project: {
|
|
automations: () => {
|
|
const forceAutomations = false
|
|
const forceNoAutomations = false
|
|
|
|
const limit = faker.number.int({ min: 0, max: 20 })
|
|
let count
|
|
if (forceNoAutomations) {
|
|
count = 0
|
|
} else {
|
|
count = forceAutomations ? limit : faker.datatype.boolean() ? limit : 0
|
|
}
|
|
|
|
return {
|
|
cursor: null,
|
|
totalCount: count,
|
|
items: times(count, () => store.get('Automation'))
|
|
} as any
|
|
},
|
|
automation: (_parent, args) => {
|
|
if (args.id === '404') {
|
|
throw new AutomationNotFoundError()
|
|
}
|
|
|
|
return store.get('Automation', { id: args.id }) as any
|
|
},
|
|
blob: () => {
|
|
return store.get('BlobMetadata') as any
|
|
}
|
|
},
|
|
Model: {
|
|
automationsStatus: async () => {
|
|
const random = faker.datatype.boolean()
|
|
return (random ? store.get('TriggeredAutomationsStatus') : null) as any
|
|
}
|
|
},
|
|
Version: {
|
|
automationsStatus: async () => {
|
|
const random = faker.datatype.boolean()
|
|
return (random ? store.get('TriggeredAutomationsStatus') : null) as any
|
|
}
|
|
},
|
|
Automation: {
|
|
creationPublicKeys: () => {
|
|
// Random sized array of string keys
|
|
return [...new Array(faker.number.int({ min: 0, max: 5 }))].map(() =>
|
|
faker.string.uuid()
|
|
)
|
|
},
|
|
runs: () => {
|
|
const forceZero = false
|
|
const count = forceZero ? 0 : faker.number.int({ min: 0, max: 20 })
|
|
|
|
return {
|
|
cursor: null,
|
|
totalCount: count,
|
|
items: times(count, () => store.get('AutomateRun'))
|
|
} as any
|
|
},
|
|
currentRevision: () => store.get('AutomationRevision') as any
|
|
},
|
|
AutomationRevision: {
|
|
triggerDefinitions: async (parent) => {
|
|
const rand = faker.number.int({ min: 0, max: 2 })
|
|
const res = (
|
|
await Promise.all([getRandomModelVersion(), getRandomModelVersion(1)])
|
|
).slice(0, rand)
|
|
|
|
return res.map(
|
|
(i): AutomationRevisionTriggerDefinitionGraphQLReturn => ({
|
|
triggerType: VersionCreationTriggerType,
|
|
triggeringId: i.model.id,
|
|
automationRevisionId: parent.id,
|
|
projectId: (store.get('Project') as any).id
|
|
})
|
|
)
|
|
},
|
|
functions: () => [store.get('AutomateFunction') as any]
|
|
},
|
|
AutomationRevisionFunction: {
|
|
parameters: () => ({}),
|
|
release: () => store.get('AutomateFunctionRelease') as any
|
|
},
|
|
AutomateRun: {
|
|
trigger: async (parent) => {
|
|
const { version } = await getRandomModelVersion()
|
|
|
|
return <AutomationRunTriggerGraphQLReturn>{
|
|
triggerType: VersionCreationTriggerType,
|
|
triggeringId: version.id,
|
|
automationRunId: parent.id
|
|
}
|
|
},
|
|
automation: () => store.get('Automation') as any,
|
|
status: () => faker.helpers.arrayElement(Object.values(AutomateRunStatus))
|
|
},
|
|
AutomateFunctionRun: {
|
|
function: () => store.get('AutomateFunction') as any,
|
|
status: () => faker.helpers.arrayElement(Object.values(AutomateRunStatus))
|
|
},
|
|
ProjectAutomationMutations: {
|
|
create: (_parent, args) => {
|
|
const {
|
|
input: { name, enabled }
|
|
} = args
|
|
const automation = store.get('Automation') as any
|
|
return {
|
|
...automation,
|
|
name,
|
|
enabled
|
|
}
|
|
},
|
|
update: (_parent, args) => {
|
|
const {
|
|
input: { id, name, enabled }
|
|
} = args
|
|
const automation = store.get('Automation') as any
|
|
return {
|
|
...automation,
|
|
id,
|
|
...(name?.length ? { name } : {}),
|
|
...(isNullOrUndefined(enabled) ? {} : { enabled })
|
|
}
|
|
},
|
|
trigger: () => faker.string.sample(10),
|
|
createRevision: () => store.get('AutomationRevision') as any
|
|
},
|
|
UserAutomateInfo: {
|
|
hasAutomateGithubApp: () => {
|
|
return faker.datatype.boolean()
|
|
},
|
|
availableGithubOrgs: () => {
|
|
// Random string array
|
|
return [...new Array(faker.number.int({ min: 0, max: 5 }))].map(() =>
|
|
faker.company.name()
|
|
)
|
|
}
|
|
},
|
|
AutomateFunction: {
|
|
creator: resolveAndCache((parent, args, ctx) => {
|
|
const rand = faker.datatype.boolean()
|
|
return getMockRef('LimitedUser', { id: !rand ? ctx.userId : undefined })
|
|
}),
|
|
repo: resolveAndCache(() => {
|
|
return {}
|
|
}),
|
|
releases: () => store.get('AutomateFunctionReleaseCollection') as any
|
|
},
|
|
AutomateFunctionRelease: {
|
|
function: () => store.get('AutomateFunction') as any
|
|
},
|
|
TriggeredAutomationsStatus: {
|
|
status: () => faker.helpers.arrayElement(Object.values(AutomateRunStatus))
|
|
},
|
|
AutomateMutations: {
|
|
createFunction: () => store.get('AutomateFunction') as any,
|
|
updateFunction: (_parent, args) => {
|
|
const {
|
|
input: { id, name, description, supportedSourceApps, tags }
|
|
} = args
|
|
const func = store.get('AutomateFunction', { id }) as any
|
|
return {
|
|
...func,
|
|
id,
|
|
...(name?.length ? { name } : {}),
|
|
...(description?.length ? { description } : {}),
|
|
...(supportedSourceApps?.length ? { supportedSourceApps } : {}),
|
|
...(tags?.length ? { tags } : {})
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
mocks: {
|
|
TriggeredAutomationsStatus: () => ({
|
|
automationRuns: () => [
|
|
...new Array(faker.datatype.number({ min: 1, max: 5 }))
|
|
]
|
|
}),
|
|
AutomationRevision: () => ({
|
|
functions: () => [undefined] // array of 1 always,
|
|
}),
|
|
Automation: () => ({
|
|
name: () => faker.company.name(),
|
|
enabled: () => faker.datatype.boolean()
|
|
}),
|
|
AutomateFunction: () => ({
|
|
name: () => faker.commerce.productName(),
|
|
isFeatured: () => faker.datatype.boolean(),
|
|
logo: () => {
|
|
const random = faker.datatype.boolean()
|
|
return random
|
|
? faker.image.imageUrl(undefined, undefined, undefined, true)
|
|
: null
|
|
},
|
|
repo: {
|
|
url: 'https://github.com/specklesystems/speckle-automate-code-compliance-window-safety',
|
|
owner: 'specklesystems',
|
|
name: 'speckle-automate-code-compliance-window-safety'
|
|
},
|
|
automationCount: () => faker.number.int({ min: 0, max: 99 }),
|
|
description: () => {
|
|
// Example markdown description
|
|
// return [
|
|
// '# Header',
|
|
// '## Subheader',
|
|
// 'Some body copy and a [link to somewhere](https://google.com)'
|
|
// ].join('\n')
|
|
return faker.lorem.sentence(20)
|
|
},
|
|
supportedSourceApps: () => {
|
|
const base = SourceAppNames
|
|
|
|
// Random assortment from base
|
|
return base.filter(() => faker.datatype.boolean())
|
|
},
|
|
tags: () => {
|
|
// Random string array
|
|
return [...new Array(faker.number.int({ min: 0, max: 5 }))].map(() =>
|
|
faker.lorem.word()
|
|
)
|
|
}
|
|
}),
|
|
AutomateFunctionRelease: () => ({
|
|
versionTag: () => {
|
|
// Fake semantic version
|
|
return `${faker.number.int({
|
|
min: 0,
|
|
max: 9
|
|
})}.${faker.number.int({
|
|
min: 0,
|
|
max: 9
|
|
})}.${faker.number.int({ min: 0, max: 9 })}`
|
|
},
|
|
commitId: () => '0c259d384a4df3cce3f24667560e5124e68f202f',
|
|
inputSchema: () => {
|
|
return {
|
|
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
$id: 'https://example.com/product.schema.json',
|
|
title: 'Product',
|
|
description: "A product from Acme's catalog",
|
|
type: 'object',
|
|
properties: {
|
|
Boolean: {
|
|
description: faker.lorem.sentence(5),
|
|
type: 'boolean'
|
|
},
|
|
'Required Boolean': {
|
|
description: faker.lorem.sentence(5),
|
|
default: true,
|
|
type: 'boolean'
|
|
},
|
|
Enum: {
|
|
description: faker.lorem.sentence(5),
|
|
type: 'string',
|
|
default: 'bar',
|
|
oneOf: [
|
|
{
|
|
const: 'foo',
|
|
title: 'FOO'
|
|
},
|
|
{
|
|
const: 'bar',
|
|
title: 'BAR'
|
|
}
|
|
]
|
|
},
|
|
Integer: {
|
|
description: faker.lorem.sentence(5),
|
|
type: 'integer'
|
|
},
|
|
'Required Integer': {
|
|
description: faker.lorem.sentence(5),
|
|
default: 2,
|
|
type: 'integer'
|
|
},
|
|
String: {
|
|
description: faker.lorem.sentence(5),
|
|
type: 'string'
|
|
},
|
|
'Required String': {
|
|
description: faker.lorem.sentence(5),
|
|
default: 'Foobar',
|
|
type: 'string'
|
|
}
|
|
},
|
|
required: ['Required Boolean', 'Required Integer', 'Required String']
|
|
}
|
|
}
|
|
}),
|
|
AutomateRun: () => ({
|
|
reason: () => faker.lorem.sentence(),
|
|
id: () => faker.string.alphanumeric(20),
|
|
createdAt: () =>
|
|
faker.date
|
|
.recent(undefined, dayjs().subtract(1, 'day').toDate())
|
|
.toISOString(),
|
|
updatedAt: () => faker.date.recent().toISOString(),
|
|
functionRuns: () => [...new Array(faker.number.int({ min: 1, max: 5 }))],
|
|
statusMessage: () => faker.lorem.sentence()
|
|
}),
|
|
AutomateFunctionRun: () => ({
|
|
contextView: () => `/`,
|
|
elapsed: () => faker.number.int({ min: 0, max: 600 }),
|
|
statusMessage: () => faker.lorem.sentence(),
|
|
results: (): Automate.AutomateTypes.ResultsSchema => {
|
|
return {
|
|
version: Automate.AutomateTypes.RESULTS_SCHEMA_VERSION,
|
|
values: {
|
|
objectResults: [],
|
|
blobIds: [...new Array(faker.number.int({ min: 0, max: 5 }))].map(() =>
|
|
faker.string.uuid()
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
ServerAutomateInfo: () => ({
|
|
availableFunctionTemplates: () => functionTemplateRepos
|
|
})
|
|
}
|
|
}
|
|
: {}
|
|
export default mocks
|