Merge pull request #3053 from specklesystems/fabians/automate-ioc-2

chore(server): automate IoC 2 - createTestAutomationFactory
This commit is contained in:
Alessandro Magionami
2024-09-23 11:17:25 +02:00
committed by GitHub
8 changed files with 111 additions and 74 deletions
@@ -1,7 +1,9 @@
import {
AutomationRecord,
AutomationRevisionWithTriggersFunctions,
AutomationTokenRecord
} from '@/modules/automate/helpers/types'
import { InsertableAutomationRevision } from '@/modules/automate/repositories/automations'
import { AuthCodePayload } from '@/modules/automate/services/authCode'
import { ProjectAutomationCreateInput } from '@/modules/core/graph/generated/graphql'
import { ContextResourceAccessRules } from '@/modules/core/helpers/token'
@@ -14,6 +16,10 @@ export type StoreAutomationToken = (
automationToken: AutomationTokenRecord
) => Promise<AutomationTokenRecord>
export type StoreAutomationRevision = (
revision: InsertableAutomationRevision
) => Promise<AutomationRevisionWithTriggersFunctions>
export type CreateStoredAuthCode = (
params: Omit<AuthCodePayload, 'code'>
) => Promise<AuthCodePayload>
@@ -24,3 +30,12 @@ export type CreateAutomation = (params: {
userId: string
userResourceAccessRules?: ContextResourceAccessRules
}) => Promise<{ automation: AutomationRecord; token: AutomationTokenRecord }>
type KeyPair = {
publicKey: string
privateKey: string
}
export type GetEncryptionKeyPair = () => Promise<KeyPair>
export type GetEncryptionKeyPairFor = (publicKey: string) => Promise<KeyPair>
@@ -22,7 +22,7 @@ import {
getProjectAutomationsItems,
getProjectAutomationsTotalCount,
storeAutomationFactory,
storeAutomationRevision,
storeAutomationRevisionFactory,
storeAutomationTokenFactory,
updateAutomationRun,
updateAutomation as updateDbAutomation,
@@ -31,7 +31,7 @@ import {
import {
createAutomationFactory,
createAutomationRevision,
createTestAutomation,
createTestAutomationFactory,
getAutomationsStatus,
updateAutomation
} from '@/modules/automate/services/automationManagement'
@@ -110,6 +110,7 @@ const { FF_AUTOMATE_MODULE_ENABLED } = getFeatureFlags()
const storeAutomation = storeAutomationFactory({ db })
const storeAutomationToken = storeAutomationTokenFactory({ db })
const storeAutomationRevision = storeAutomationRevisionFactory({ db })
export = (FF_AUTOMATE_MODULE_ENABLED
? {
@@ -532,11 +533,13 @@ export = (FF_AUTOMATE_MODULE_ENABLED
return automationRunId
},
async createTestAutomation(parent, { input }, ctx) {
const create = createTestAutomation({
const create = createTestAutomationFactory({
getEncryptionKeyPair,
getFunction,
storeAutomation,
storeAutomationRevision
storeAutomationRevision,
validateStreamAccess,
automationsEventsEmit: AutomationsEmitter.emit
})
return await create({
@@ -1,5 +1,6 @@
import {
StoreAutomation,
StoreAutomationRevision,
StoreAutomationToken
} from '@/modules/automate/domain/operations'
import {
@@ -57,7 +58,13 @@ import { SetOptional, SetRequired } from 'type-fest'
const tables = {
automations: (db: Knex) => db<AutomationRecord>(Automations.name),
automationTokens: (db: Knex) => db<AutomationTokenRecord>(AutomationTokens.name)
automationTokens: (db: Knex) => db<AutomationTokenRecord>(AutomationTokens.name),
automationRevisions: (db: Knex) =>
db<AutomationRevisionRecord>(AutomationRevisions.name),
automationRevisionFunctions: (db: Knex) =>
db<AutomateRevisionFunctionRecord>(AutomationRevisionFunctions.name),
automationTriggers: (db: Knex) =>
db<AutomationTriggerDefinitionRecord>(AutomationTriggers.name)
}
export const generateRevisionId = () => cryptoRandomString({ length: 10 })
@@ -337,57 +344,61 @@ export async function updateAutomationRevision(
return ret
}
export type StoredInsertableAutomationRevision = Awaited<
ReturnType<typeof storeAutomationRevision>
>
export type StoredInsertableAutomationRevision = AutomationRevisionWithTriggersFunctions
export async function storeAutomationRevision(revision: InsertableAutomationRevision) {
const id = revision.id || generateRevisionId()
const rev = _.pick(revision, AutomationRevisions.withoutTablePrefix.cols)
const [newRev] = await AutomationRevisions.knex()
.insert({
...rev,
id
})
.returning<AutomationRevisionRecord[]>('*')
const [functions, triggers] = await Promise.all([
AutomationRevisionFunctions.knex()
.insert(
revision.functions.map(
(f): AutomateRevisionFunctionRecord => ({
...f,
automationRevisionId: id
})
export const storeAutomationRevisionFactory =
(deps: { db: Knex }): StoreAutomationRevision =>
async (revision: InsertableAutomationRevision) => {
const id = revision.id || generateRevisionId()
const rev = _.pick(revision, AutomationRevisions.withoutTablePrefix.cols)
const [newRev] = await tables
.automationRevisions(deps.db)
.insert({
...rev,
id
})
.returning('*')
const [functions, triggers] = await Promise.all([
tables
.automationRevisionFunctions(deps.db)
.insert(
revision.functions.map(
(f): AutomateRevisionFunctionRecord => ({
...f,
automationRevisionId: id
})
)
)
)
.returning<AutomateRevisionFunctionRecord[]>('*'),
AutomationTriggers.knex()
.insert(
revision.triggers.map(
(t): AutomationTriggerDefinitionRecord => ({
...t,
automationRevisionId: id
})
.returning('*'),
tables
.automationTriggers(deps.db)
.insert(
revision.triggers.map(
(t): AutomationTriggerDefinitionRecord => ({
...t,
automationRevisionId: id
})
)
)
)
.returning<AutomationTriggerDefinitionRecord[]>('*'),
// Unset 'active in revision' for all other revisions
...(revision.active
? [
AutomationRevisions.knex()
.where(AutomationRevisions.col.automationId, newRev.automationId)
.andWhereNot(AutomationRevisions.col.id, newRev.id)
.update(AutomationRevisions.withoutTablePrefix.col.active, false)
]
: [])
])
.returning('*'),
// Unset 'active in revision' for all other revisions
...(revision.active
? [
tables
.automationRevisions(deps.db)
.where(AutomationRevisions.col.automationId, newRev.automationId)
.andWhereNot(AutomationRevisions.col.id, newRev.id)
.update(AutomationRevisions.withoutTablePrefix.col.active, false)
]
: [])
])
return {
...newRev,
functions,
triggers
return {
...newRev,
functions,
triggers
}
}
}
export async function getAutomationToken(
automationId: string
@@ -4,7 +4,6 @@ import {
InsertableAutomationRevisionTrigger,
getAutomation,
getLatestVersionAutomationRuns,
storeAutomationRevision,
updateAutomation as updateDbAutomation
} from '@/modules/automate/repositories/automations'
import { getServerOrigin } from '@/modules/shared/helpers/envHelper'
@@ -41,10 +40,7 @@ import { getBranchesByIds } from '@/modules/core/repositories/branches'
import { keyBy, uniq } from 'lodash'
import { resolveStatusFromFunctionRunStatuses } from '@/modules/automate/services/runsManagement'
import { TriggeredAutomationsStatusGraphQLReturn } from '@/modules/automate/helpers/graphTypes'
import {
getEncryptionKeyPair,
getFunctionInputDecryptor
} from '@/modules/automate/services/encryption'
import { getFunctionInputDecryptor } from '@/modules/automate/services/encryption'
import { LibsodiumEncryptionError } from '@/modules/shared/errors/encryption'
import { validateInputAgainstFunctionSchema } from '@/modules/automate/utils/inputSchemaValidator'
import {
@@ -55,7 +51,9 @@ import { validateAutomationName } from '@/modules/automate/utils/automationConfi
import {
CreateAutomation,
CreateStoredAuthCode,
GetEncryptionKeyPair,
StoreAutomation,
StoreAutomationRevision,
StoreAutomationToken
} from '@/modules/automate/domain/operations'
@@ -139,17 +137,19 @@ export const createAutomationFactory =
}
export type CreateTestAutomationDeps = {
getEncryptionKeyPair: typeof getEncryptionKeyPair
getEncryptionKeyPair: GetEncryptionKeyPair
getFunction: typeof getFunction
storeAutomation: StoreAutomation
storeAutomationRevision: typeof storeAutomationRevision
storeAutomationRevision: StoreAutomationRevision
validateStreamAccess: typeof validateStreamAccess
automationsEventsEmit: AutomationsEventsEmit
}
/**
* Create a test automation and its first revision in one request.
* TODO: Reduce code duplication w/ createAutomation
*/
export const createTestAutomation =
export const createTestAutomationFactory =
(deps: CreateTestAutomationDeps) =>
async (params: {
input: ProjectTestAutomationCreateInput
@@ -167,7 +167,9 @@ export const createTestAutomation =
getEncryptionKeyPair,
getFunction,
storeAutomation,
storeAutomationRevision
storeAutomationRevision,
validateStreamAccess,
automationsEventsEmit
} = deps
validateAutomationName(name)
@@ -233,7 +235,7 @@ export const createTestAutomation =
publicKey: encryptionKeyPair.publicKey
})
await AutomationsEmitter.emit(AutomationsEmitter.events.CreatedRevision, {
await automationsEventsEmit(AutomationsEmitter.events.CreatedRevision, {
automation: automationRecord,
revision: automationRevisionRecord
})
@@ -382,8 +384,8 @@ const validateNewRevisionFunctions =
export type CreateAutomationRevisionDeps = {
getAutomation: typeof getAutomation
storeAutomationRevision: typeof storeAutomationRevision
getEncryptionKeyPair: typeof getEncryptionKeyPair
storeAutomationRevision: StoreAutomationRevision
getEncryptionKeyPair: GetEncryptionKeyPair
getFunctionInputDecryptor: ReturnType<typeof getFunctionInputDecryptor>
getFunctionReleases: typeof getFunctionReleases
} & ValidateNewTriggerDefinitionsDeps &
@@ -18,6 +18,10 @@ import { LibsodiumEncryptionError } from '@/modules/shared/errors/encryption'
import { Merge } from 'type-fest'
import { convertFunctionReleaseToGraphQLReturn } from '@/modules/automate/services/functionManagement'
import { redactWriteOnlyInputData } from '@/modules/automate/utils/jsonSchemaRedactor'
import {
GetEncryptionKeyPair,
GetEncryptionKeyPairFor
} from '@/modules/automate/domain/operations'
type KeysFileContents = Array<KeyPair>
@@ -49,7 +53,7 @@ const getEncryptionKeys = async () => {
return keys
}
export const getEncryptionKeyPair = async () => {
export const getEncryptionKeyPair: GetEncryptionKeyPair = async () => {
return (await getEncryptionKeys())[0]
}
@@ -57,7 +61,9 @@ export const getEncryptionPublicKey = async () => {
return (await getEncryptionKeyPair()).publicKey
}
export const getEncryptionKeyPairFor = async (publicKey: string) => {
export const getEncryptionKeyPairFor: GetEncryptionKeyPairFor = async (
publicKey: string
) => {
const keyPairs = await getEncryptionKeys()
const keyPair = keyPairs.find((keyPair) => keyPair.publicKey === publicKey)
if (!keyPair) {
@@ -139,7 +145,7 @@ export const getFunctionInputDecryptor =
}
export type GetFunctionInputsForFrontendDeps = {
getEncryptionKeyPairFor: typeof getEncryptionKeyPairFor
getEncryptionKeyPairFor: GetEncryptionKeyPairFor
redactWriteOnlyInputData: typeof redactWriteOnlyInputData
} & GetFunctionInputDecryptorDeps
@@ -40,12 +40,10 @@ import { validateStreamAccess } from '@/modules/core/services/streams/streamAcce
import { ContextResourceAccessRules } from '@/modules/core/helpers/token'
import { TokenResourceIdentifierType } from '@/modules/core/graph/generated/graphql'
import { automateLogger } from '@/logging/logging'
import {
getEncryptionKeyPairFor,
getFunctionInputDecryptor
} from '@/modules/automate/services/encryption'
import { getFunctionInputDecryptor } from '@/modules/automate/services/encryption'
import { LibsodiumEncryptionError } from '@/modules/shared/errors/encryption'
import { AutomateRunsEmitter } from '@/modules/automate/events/runs'
import { GetEncryptionKeyPairFor } from '@/modules/automate/domain/operations'
export type OnModelVersionCreateDeps = {
getAutomation: typeof getAutomation
@@ -126,7 +124,7 @@ type InsertableAutomationRunWithExtendedFunctionRuns = Merge<
>
type CreateAutomationRunDataDeps = {
getEncryptionKeyPairFor: typeof getEncryptionKeyPairFor
getEncryptionKeyPairFor: GetEncryptionKeyPairFor
getFunctionInputDecryptor: ReturnType<typeof getFunctionInputDecryptor>
}
@@ -37,14 +37,14 @@ import {
getFullAutomationRunById,
getAutomationTriggerDefinitions,
getFunctionRun,
storeAutomationRevision,
updateAutomation,
updateAutomationRevision,
updateAutomationRun,
upsertAutomationRun,
upsertAutomationFunctionRun,
storeAutomationFactory,
storeAutomationTokenFactory
storeAutomationTokenFactory,
storeAutomationRevisionFactory
} from '@/modules/automate/repositories/automations'
import { beforeEachContext, truncateTables } from '@/test/hooks'
import { Automate } from '@speckle/shared'
@@ -77,6 +77,7 @@ const { FF_AUTOMATE_MODULE_ENABLED } = getFeatureFlags()
const storeAutomation = storeAutomationFactory({ db })
const storeAutomationToken = storeAutomationTokenFactory({ db })
const storeAutomationRevision = storeAutomationRevisionFactory({ db })
;(FF_AUTOMATE_MODULE_ENABLED ? describe : describe.skip)(
'Automate triggers @automate',
@@ -1,7 +1,7 @@
import {
getAutomation,
storeAutomationFactory,
storeAutomationRevision,
storeAutomationRevisionFactory,
storeAutomationTokenFactory
} from '@/modules/automate/repositories/automations'
import {
@@ -44,6 +44,7 @@ import { validateStreamAccess } from '@/modules/core/services/streams/streamAcce
const storeAutomation = storeAutomationFactory({ db })
const storeAutomationToken = storeAutomationTokenFactory({ db })
const storeAutomationRevision = storeAutomationRevisionFactory({ db })
export const generateFunctionId = () => cryptoRandomString({ length: 10 })
export const generateFunctionReleaseId = () => cryptoRandomString({ length: 10 })