budibase/packages/server/src/tests/utilities/structures.ts

762 lines
17 KiB
TypeScript

import { permissions, roles, utils } from "@budibase/backend-core"
import { createHomeScreen } from "../../constants/screens"
import { EMPTY_LAYOUT } from "../../constants/layouts"
import { cloneDeep } from "lodash/fp"
import {
BUILTIN_ACTION_DEFINITIONS,
TRIGGER_DEFINITIONS,
} from "../../automations"
import {
AIOperationEnum,
AutoFieldSubType,
Automation,
AutomationActionStepId,
AutomationEventType,
AutomationResults,
AutomationStatus,
AutomationStep,
AutomationStepType,
AutomationTrigger,
AutomationTriggerStepId,
BBReferenceFieldSubType,
CreateViewRequest,
Datasource,
FieldSchema,
FieldType,
INTERNAL_TABLE_SOURCE_ID,
JsonFieldSubType,
LoopStepType,
Query,
Role,
SourceName,
Table,
TableSourceType,
Webhook,
WebhookActionType,
} from "@budibase/types"
import { LoopInput } from "../../definitions/automations"
import { merge } from "lodash"
import { generator } from "@budibase/backend-core/tests"
const { BUILTIN_ROLE_IDS } = roles
export function tableForDatasource(
datasource?: Datasource,
...extra: Partial<Table>[]
): Table {
return merge(
{
name: generator.guid().substring(0, 10),
type: "table",
sourceType: datasource
? TableSourceType.EXTERNAL
: TableSourceType.INTERNAL,
sourceId: datasource ? datasource._id! : INTERNAL_TABLE_SOURCE_ID,
schema: {},
},
...extra
)
}
export function basicTable(
datasource?: Datasource,
...extra: Partial<Table>[]
): Table {
return tableForDatasource(
datasource,
{
name: "TestTable",
schema: {
name: {
type: FieldType.STRING,
name: "name",
constraints: {
type: "string",
},
},
description: {
type: FieldType.STRING,
name: "description",
constraints: {
type: "string",
},
},
},
},
...extra
)
}
export function basicTableWithAttachmentField(
datasource?: Datasource,
...extra: Partial<Table>[]
): Table {
return tableForDatasource(
datasource,
{
name: "TestTable",
schema: {
file_attachment: {
type: FieldType.ATTACHMENTS,
name: "description",
constraints: {
type: "array",
},
},
single_file_attachment: {
type: FieldType.ATTACHMENT_SINGLE,
name: "description",
},
},
},
...extra
)
}
export function basicView(tableId: string) {
return {
tableId,
name: "ViewTest",
}
}
export function filterView(tableId: string) {
return {
...basicView(tableId),
filters: [
{
value: 0,
condition: "MT",
key: "count",
},
],
}
}
export function calculationView(tableId: string) {
return {
...basicView(tableId),
field: "count",
calculation: "sum",
}
}
export function view(tableId: string) {
return {
...filterView(tableId),
...calculationView(tableId),
}
}
function viewV2CreateRequest(tableId: string): CreateViewRequest {
return {
tableId,
name: generator.guid(),
}
}
export const viewV2 = {
createRequest: viewV2CreateRequest,
}
export function automationStep(
actionDefinition = BUILTIN_ACTION_DEFINITIONS.CREATE_ROW
): AutomationStep {
return {
id: utils.newid(),
...actionDefinition,
stepId: AutomationActionStepId.CREATE_ROW,
inputs: { row: {} },
}
}
export function automationTrigger(
triggerDefinition = TRIGGER_DEFINITIONS.ROW_SAVED
): AutomationTrigger {
return {
id: utils.newid(),
...triggerDefinition,
} as AutomationTrigger
}
export function newAutomation({
steps,
trigger,
}: { steps?: AutomationStep[]; trigger?: AutomationTrigger } = {}) {
const automation = basicAutomation()
if (trigger) {
automation.definition.trigger = trigger
} else {
automation.definition.trigger = automationTrigger()
}
if (steps) {
automation.definition.steps = steps
} else {
automation.definition.steps = [automationStep()]
}
return automation
}
export function rowActionAutomation() {
const automation = newAutomation({
trigger: {
...automationTrigger(),
stepId: AutomationTriggerStepId.ROW_ACTION,
},
})
return automation
}
export function basicAutomation(appId?: string): Automation {
return {
name: "My Automation",
screenId: "kasdkfldsafkl",
live: true,
uiTree: {},
definition: {
trigger: {
stepId: AutomationTriggerStepId.APP,
name: "test",
tagline: "test",
icon: "test",
description: "test",
type: AutomationStepType.TRIGGER,
id: "test",
inputs: {
fields: {},
},
schema: {
inputs: {
properties: {},
},
outputs: {
properties: {},
},
},
},
steps: [],
},
type: "automation",
appId: appId!,
}
}
export function serverLogAutomation(appId?: string): Automation {
return {
name: "My Automation",
screenId: "kasdkfldsafkl",
live: true,
uiTree: {},
definition: {
trigger: {
stepId: AutomationTriggerStepId.APP,
name: "test",
tagline: "test",
icon: "test",
description: "test",
type: AutomationStepType.TRIGGER,
id: "test",
inputs: { fields: {} },
schema: {
inputs: {
properties: {},
},
outputs: {
properties: {},
},
},
},
steps: [
{
stepId: AutomationActionStepId.SERVER_LOG,
name: "Backend log",
tagline: "Console log a value in the backend",
icon: "Monitoring",
description: "Logs the given text to the server (using console.log)",
internal: true,
features: {
LOOPING: true,
},
inputs: {
text: "log statement",
},
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
id: "y8lkZbeSe",
type: AutomationStepType.ACTION,
},
],
},
type: "automation",
appId: appId!,
}
}
export function loopAutomation(
tableId: string,
loopOpts?: LoopInput
): Automation {
if (!loopOpts) {
loopOpts = {
option: LoopStepType.ARRAY,
binding: "{{ steps.1.rows }}",
}
}
const automation: any = {
name: "looping",
type: "automation",
definition: {
steps: [
{
id: "b",
type: "ACTION",
stepId: AutomationActionStepId.QUERY_ROWS,
internal: true,
inputs: {
tableId,
},
schema: BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS.schema,
},
{
id: "c",
type: "ACTION",
stepId: AutomationActionStepId.LOOP,
internal: true,
inputs: loopOpts,
blockToLoop: "d",
schema: BUILTIN_ACTION_DEFINITIONS.LOOP.schema,
},
{
id: "d",
type: "ACTION",
internal: true,
stepId: AutomationActionStepId.SERVER_LOG,
inputs: {
text: "log statement",
},
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
},
],
trigger: {
id: "a",
type: "TRIGGER",
event: AutomationEventType.ROW_SAVE,
stepId: AutomationTriggerStepId.ROW_SAVED,
inputs: {
tableId,
},
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
},
},
}
return automation as Automation
}
export function collectAutomation(tableId?: string): Automation {
const automation: any = {
name: "looping",
type: "automation",
definition: {
steps: [
{
id: "b",
type: "ACTION",
internal: true,
stepId: AutomationActionStepId.EXECUTE_SCRIPT,
inputs: {
code: "return [1,2,3]",
},
schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema,
},
{
id: "c",
type: "ACTION",
internal: true,
stepId: AutomationActionStepId.COLLECT,
inputs: {
collection: "{{ literal steps.1.value }}",
},
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
},
],
trigger: {
id: "a",
type: "TRIGGER",
event: AutomationEventType.ROW_SAVE,
stepId: AutomationTriggerStepId.ROW_SAVED,
inputs: {
tableId,
},
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
},
},
}
return automation
}
export function filterAutomation(appId: string, tableId?: string): Automation {
const automation: Automation = {
name: "looping",
type: "automation",
appId,
definition: {
steps: [
{
name: "Filter Step",
tagline: "An automation filter step",
description: "A filter automation",
id: "b",
icon: "Icon",
type: AutomationStepType.ACTION,
internal: true,
stepId: AutomationActionStepId.FILTER,
inputs: { field: "name", value: "test", condition: "EQ" },
schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema,
},
],
trigger: {
name: "trigger Step",
tagline: "An automation trigger",
description: "A trigger",
icon: "Icon",
id: "a",
type: AutomationStepType.TRIGGER,
event: "row:save",
stepId: AutomationTriggerStepId.ROW_SAVED,
inputs: {
tableId: tableId!,
},
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
},
},
}
return automation
}
export function updateRowAutomationWithFilters(
appId: string,
tableId: string
): Automation {
return {
name: "updateRowWithFilters",
type: "automation",
appId,
definition: {
steps: [
{
name: "Filter Step",
tagline: "An automation filter step",
description: "A filter automation",
icon: "Icon",
id: "b",
type: AutomationStepType.ACTION,
internal: true,
stepId: AutomationActionStepId.SERVER_LOG,
inputs: { text: "log statement" },
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
},
],
trigger: {
name: "trigger Step",
tagline: "An automation trigger",
description: "A trigger",
icon: "Icon",
id: "a",
type: AutomationStepType.TRIGGER,
event: "row:update",
stepId: AutomationTriggerStepId.ROW_UPDATED,
inputs: { tableId },
schema: TRIGGER_DEFINITIONS.ROW_UPDATED.schema,
},
},
}
}
export function basicAutomationResults(
automationId: string
): AutomationResults {
return {
automationId,
status: AutomationStatus.SUCCESS,
trigger: "trigger",
steps: [
{
stepId: AutomationActionStepId.SERVER_LOG,
inputs: {},
outputs: {},
},
],
}
}
export function basicRow(tableId: string) {
return {
name: "Test Contact",
description: "original description",
tableId: tableId,
}
}
export function basicLinkedRow(
tableId: string,
linkedRowId: string,
linkField: string = "link"
) {
// this is based on the basic linked tables you get from the test configuration
return {
...basicRow(tableId),
[linkField]: [linkedRowId],
}
}
export function basicRole(): Role {
return {
name: `NewRole_${utils.newid()}`,
inherits: roles.BUILTIN_ROLE_IDS.BASIC,
permissionId: permissions.BuiltinPermissionID.READ_ONLY,
permissions: {},
version: "name",
}
}
export function basicDatasource(): { datasource: Datasource } {
return {
datasource: {
type: "datasource",
name: "Test",
source: SourceName.POSTGRES,
config: {},
},
}
}
export function basicQuery(datasourceId: string): Query {
return {
datasourceId,
name: "New Query",
parameters: [],
fields: {},
schema: {},
queryVerb: "read",
transformer: null,
readable: true,
}
}
export function basicUser(role: string) {
return {
email: "bill@bill.com",
password: "yeeooo",
roleId: role,
}
}
export function basicScreen(route: string = "/") {
return createHomeScreen({
roleId: BUILTIN_ROLE_IDS.BASIC,
route,
})
}
export function powerScreen(route: string = "/") {
return createHomeScreen({
roleId: BUILTIN_ROLE_IDS.POWER,
route,
})
}
export function customScreen(config: { roleId: string; route: string }) {
return createHomeScreen(config)
}
export function basicLayout() {
return cloneDeep(EMPTY_LAYOUT)
}
export function basicWebhook(automationId: string): Webhook {
return {
live: true,
name: "webhook",
action: {
type: WebhookActionType.AUTOMATION,
target: automationId,
},
}
}
export function basicEnvironmentVariable(
name: string,
prod: string,
dev?: string
) {
return {
name,
production: prod,
development: dev || prod,
}
}
export function fullSchemaWithoutLinks({
allRequired,
}: {
allRequired?: boolean
}): {
[type in Exclude<FieldType, FieldType.LINK>]: FieldSchema & { type: type }
} {
return {
[FieldType.STRING]: {
name: "string",
type: FieldType.STRING,
constraints: {
presence: allRequired,
},
},
[FieldType.LONGFORM]: {
name: "longform",
type: FieldType.LONGFORM,
constraints: {
presence: allRequired,
},
},
[FieldType.OPTIONS]: {
name: "options",
type: FieldType.OPTIONS,
constraints: {
presence: allRequired,
inclusion: ["option 1", "option 2", "option 3", "option 4"],
},
},
[FieldType.ARRAY]: {
name: "array",
type: FieldType.ARRAY,
constraints: {
presence: allRequired,
type: JsonFieldSubType.ARRAY,
inclusion: ["options 1", "options 2", "options 3", "options 4"],
},
},
[FieldType.NUMBER]: {
name: "number",
type: FieldType.NUMBER,
constraints: {
presence: allRequired,
},
},
[FieldType.BOOLEAN]: {
name: "boolean",
type: FieldType.BOOLEAN,
constraints: {
presence: allRequired,
},
},
[FieldType.DATETIME]: {
name: "datetime",
type: FieldType.DATETIME,
dateOnly: true,
timeOnly: false,
constraints: {
presence: allRequired,
},
},
[FieldType.FORMULA]: {
name: "formula",
type: FieldType.FORMULA,
formula: "any formula",
constraints: {
presence: allRequired,
},
},
[FieldType.AI]: {
name: "ai",
type: FieldType.AI,
operation: AIOperationEnum.PROMPT,
prompt: "Translate this into German :'{{ product }}'",
},
[FieldType.BARCODEQR]: {
name: "barcodeqr",
type: FieldType.BARCODEQR,
constraints: {
presence: allRequired,
},
},
[FieldType.BIGINT]: {
name: "bigint",
type: FieldType.BIGINT,
constraints: {
presence: allRequired,
},
},
[FieldType.BB_REFERENCE]: {
name: "user",
type: FieldType.BB_REFERENCE,
subtype: BBReferenceFieldSubType.USER,
constraints: {
presence: allRequired,
},
},
[FieldType.BB_REFERENCE_SINGLE]: {
name: "users",
type: FieldType.BB_REFERENCE_SINGLE,
subtype: BBReferenceFieldSubType.USER,
constraints: {
presence: allRequired,
},
},
[FieldType.ATTACHMENTS]: {
name: "attachments",
type: FieldType.ATTACHMENTS,
constraints: {
presence: allRequired,
},
},
[FieldType.ATTACHMENT_SINGLE]: {
name: "attachment_single",
type: FieldType.ATTACHMENT_SINGLE,
constraints: {
presence: allRequired,
},
},
[FieldType.AUTO]: {
name: "auto",
type: FieldType.AUTO,
subtype: AutoFieldSubType.AUTO_ID,
autocolumn: true,
constraints: {
presence: allRequired,
},
},
[FieldType.JSON]: {
name: "json",
type: FieldType.JSON,
constraints: {
presence: allRequired,
},
},
[FieldType.INTERNAL]: {
name: "internal",
type: FieldType.INTERNAL,
constraints: {
presence: allRequired,
},
},
[FieldType.SIGNATURE_SINGLE]: {
name: "signature_single",
type: FieldType.SIGNATURE_SINGLE,
constraints: {
presence: allRequired,
},
},
}
}
export function basicAttachment() {
return {
key: generator.guid(),
name: generator.word(),
extension: generator.word(),
size: generator.natural(),
url: `/${generator.guid()}`,
}
}