Run integration suite in CI again / auto detect tenancy / refactors (#10209)
* qa-core-ci-fixes * global setup and teardown wip * Updates to logs and setup * Remove date and console mocking * Update CI to spin up minimal dev env * Update readme * Fix scopeBackend.sh * Ensure docker services are initialised before starting worker * Lint * Fix admin user being created on startup (#10219) * use regular bootstrap and build * Lint * Temp: re-use global setup to get around app limit in QA
This commit is contained in:
parent
18ccf134b9
commit
77ffb8d86d
|
@ -77,28 +77,21 @@ jobs:
|
|||
- run: yarn bootstrap
|
||||
- run: yarn test:pro
|
||||
|
||||
# integration-test:
|
||||
# runs-on: ubuntu-latest
|
||||
# services:
|
||||
# couchdb:
|
||||
# image: ibmcom/couchdb3
|
||||
# env:
|
||||
# COUCHDB_PASSWORD: budibase
|
||||
# COUCHDB_USER: budibase
|
||||
# ports:
|
||||
# - 4567:5984
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Use Node.js 14.x
|
||||
# uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: 14.x
|
||||
# - name: Install Pro
|
||||
# run: yarn install:pro $BRANCH $BASE_BRANCH
|
||||
# - run: yarn
|
||||
# - run: yarn bootstrap
|
||||
# - run: yarn build
|
||||
# - run: |
|
||||
# cd qa-core
|
||||
# yarn
|
||||
# yarn api:test:ci
|
||||
integration-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14.x
|
||||
- name: Install Pro
|
||||
run: yarn install:pro $BRANCH $BASE_BRANCH
|
||||
- run: yarn && yarn bootstrap && yarn build
|
||||
- run: |
|
||||
cd qa-core
|
||||
yarn setup
|
||||
yarn test:ci
|
||||
env:
|
||||
BB_ADMIN_USER_EMAIL: admin
|
||||
BB_ADMIN_USER_PASSWORD: admin
|
|
@ -23,10 +23,12 @@
|
|||
},
|
||||
"scripts": {
|
||||
"setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",
|
||||
"bootstrap": "lerna bootstrap && lerna link && ./scripts/link-dependencies.sh",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"postbootstrap": "lerna link && ./scripts/link-dependencies.sh",
|
||||
"build": "lerna run --stream build",
|
||||
"build:dev": "lerna run --stream prebuild && tsc --build --watch --preserveWatchOutput",
|
||||
"build:backend": "lerna run --stream build --ignore @budibase/client --ignore @budibase/bbui --ignore @budibase/builder --ignore @budibase/cli",
|
||||
"backend:bootstrap": "./scripts/scopeBackend.sh 'lerna bootstrap' && yarn run postbootstrap",
|
||||
"backend:build": "./scripts/scopeBackend.sh 'lerna run --stream build'",
|
||||
"build:sdk": "lerna run --stream build:sdk",
|
||||
"deps:circular": "madge packages/server/dist/index.js packages/worker/src/index.ts packages/backend-core/dist/src/index.js packages/cli/src/index.js --circular",
|
||||
"release": "lerna publish ${RELEASE_VERSION_TYPE:-patch} --yes --force-publish && yarn release:pro",
|
||||
|
@ -44,6 +46,7 @@
|
|||
"dev": "yarn run kill-all && lerna link && lerna run --stream --parallel dev:builder --concurrency 1 --stream",
|
||||
"dev:noserver": "yarn run kill-builder && lerna link && lerna run --stream dev:stack:up && lerna run --stream --parallel dev:builder --concurrency 1 --ignore @budibase/backend-core --ignore @budibase/server --ignore @budibase/worker",
|
||||
"dev:server": "yarn run kill-server && lerna run --stream --parallel dev:builder --concurrency 1 --scope @budibase/backend-core --scope @budibase/worker --scope @budibase/server",
|
||||
"dev:built": "cd packages/server && yarn dev:stack:up && cd ../../ && lerna run --stream --parallel dev:built",
|
||||
"test": "lerna run --stream test --stream",
|
||||
"test:pro": "bash scripts/pro/test.sh",
|
||||
"lint:eslint": "eslint packages && eslint qa-core",
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
export * as correlation from "./correlation/correlation"
|
||||
export { default as logger } from "./pino/logger"
|
||||
export * from "./alerts"
|
||||
|
||||
// turn off or on context logging i.e. tenantId, appId etc
|
||||
export let LOG_CONTEXT = true
|
||||
|
|
|
@ -3,6 +3,7 @@ import pino, { LoggerOptions } from "pino"
|
|||
import * as context from "../../context"
|
||||
import * as correlation from "../correlation"
|
||||
import { IdentityType } from "@budibase/types"
|
||||
import { LOG_CONTEXT } from "../index"
|
||||
|
||||
// LOGGER
|
||||
|
||||
|
@ -77,14 +78,22 @@ function getLogParams(args: any[]): [MergingObject, string] {
|
|||
|
||||
const identity = getIdentity()
|
||||
|
||||
let contextObject = {}
|
||||
|
||||
if (LOG_CONTEXT) {
|
||||
contextObject = {
|
||||
tenantId: getTenantId(),
|
||||
appId: getAppId(),
|
||||
identityId: identity?._id,
|
||||
identityType: identity?.type,
|
||||
correlationId: correlation.getId(),
|
||||
}
|
||||
}
|
||||
|
||||
const mergingObject = {
|
||||
objects: objects.length ? objects : undefined,
|
||||
tenantId: getTenantId(),
|
||||
appId: getAppId(),
|
||||
identityId: identity?._id,
|
||||
identityType: identity?.type,
|
||||
correlationId: correlation.getId(),
|
||||
err: error,
|
||||
...contextObject,
|
||||
}
|
||||
|
||||
return [mergingObject, message]
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"reporterEnabled": "mochawesome",
|
||||
"mochawesomeReporterOptions": {
|
||||
"reportDir": "cypress/reports",
|
||||
"quiet": true,
|
||||
"overwrite": false,
|
||||
"html": false,
|
||||
"json": true
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
const testConfig = require("./testConfig.json")
|
||||
|
||||
// normal development system
|
||||
const SERVER_PORT = testConfig.env.PORT
|
||||
const WORKER_PORT = testConfig.env.WORKER_PORT
|
||||
|
||||
if (!process.env.NODE_ENV) {
|
||||
process.env.NODE_ENV = "cypress"
|
||||
}
|
||||
process.env.ENABLE_ANALYTICS = "0"
|
||||
process.env.JWT_SECRET = testConfig.env.JWT_SECRET
|
||||
process.env.SELF_HOSTED = 1
|
||||
process.env.WORKER_URL = `http://localhost:${WORKER_PORT}/`
|
||||
process.env.APPS_URL = `http://localhost:${SERVER_PORT}/`
|
||||
process.env.MINIO_URL = `http://localhost:4004`
|
||||
process.env.MINIO_ACCESS_KEY = "budibase"
|
||||
process.env.MINIO_SECRET_KEY = "budibase"
|
||||
process.env.COUCH_DB_USER = "budibase"
|
||||
process.env.COUCH_DB_PASSWORD = "budibase"
|
||||
process.env.INTERNAL_API_KEY = "budibase"
|
||||
process.env.ALLOW_DEV_AUTOMATIONS = 1
|
||||
process.env.MOCK_REDIS = 1
|
||||
|
||||
// Stop info logs polluting test outputs
|
||||
process.env.LOG_LEVEL = "error"
|
||||
|
||||
exports.run = (serverLoc = "../server/dist", workerLoc = "../worker/dist") => {
|
||||
// require("dotenv").config({ path: resolve(dir, ".env") })
|
||||
// don't make this a variable or top level require
|
||||
// it will cause environment module to be loaded prematurely
|
||||
|
||||
// override the port with the worker port temporarily
|
||||
process.env.PORT = WORKER_PORT
|
||||
require(workerLoc)
|
||||
|
||||
// override the port with the server port
|
||||
process.env.PORT = SERVER_PORT
|
||||
require(serverLoc)
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
exports.run()
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:4100",
|
||||
"projectId": "bmbemn",
|
||||
"reporterOptions": {
|
||||
"configFile": "reporterConfig.json"
|
||||
},
|
||||
"env": {
|
||||
"PORT": "4100",
|
||||
"WORKER_PORT": "4200",
|
||||
"JWT_SECRET": "test",
|
||||
"HOST_IP": ""
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
// @ts-ignore
|
||||
import { run } from "../setup"
|
||||
|
||||
run("../server/src/index", "../worker/src/index")
|
|
@ -26,6 +26,7 @@
|
|||
"dev:stack:down": "node scripts/dev/manage.js down",
|
||||
"dev:stack:nuke": "node scripts/dev/manage.js nuke",
|
||||
"dev:builder": "yarn run dev:stack:up && nodemon",
|
||||
"dev:built": "yarn run dev:stack:up && yarn run run:docker",
|
||||
"specs": "ts-node specs/generate.ts && openapi-typescript specs/openapi.yaml --output src/definitions/openapi.ts",
|
||||
"initialise": "node scripts/initialise.js",
|
||||
"env:multi:enable": "node scripts/multiTenancy.js enable",
|
||||
|
|
|
@ -45,7 +45,8 @@ async function init() {
|
|||
BB_ADMIN_USER_PASSWORD: "",
|
||||
PLUGINS_DIR: "",
|
||||
TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
|
||||
HTTP_LOGGING: 0,
|
||||
HTTP_MIGRATIONS: "0",
|
||||
HTTP_LOGGING: "0",
|
||||
}
|
||||
let envFile = ""
|
||||
Object.keys(envFileJson).forEach(key => {
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/******************************************************
|
||||
* This script just makes it easy to re-create *
|
||||
* a cypress like environment for testing the backend *
|
||||
******************************************************/
|
||||
import path from "path"
|
||||
const tmpdir = path.join(require("os").tmpdir(), ".budibase")
|
||||
|
||||
const SERVER_PORT = "4100"
|
||||
const WORKER_PORT = "4200"
|
||||
|
||||
// @ts-ignore
|
||||
process.env.NODE_ENV = "cypress"
|
||||
process.env.ENABLE_ANALYTICS = "0"
|
||||
process.env.JWT_SECRET = "budibase"
|
||||
process.env.COUCH_URL = `leveldb://${tmpdir}/.data/`
|
||||
process.env.SELF_HOSTED = "1"
|
||||
process.env.WORKER_URL = `http://localhost:${WORKER_PORT}/`
|
||||
process.env.MINIO_URL = `http://localhost:4004`
|
||||
process.env.MINIO_ACCESS_KEY = "budibase"
|
||||
process.env.MINIO_SECRET_KEY = "budibase"
|
||||
process.env.COUCH_DB_USER = "budibase"
|
||||
process.env.COUCH_DB_PASSWORD = "budibase"
|
||||
process.env.INTERNAL_API_KEY = "budibase"
|
||||
process.env.ALLOW_DEV_AUTOMATIONS = "1"
|
||||
|
||||
// don't make this a variable or top level require
|
||||
// it will cause environment module to be loaded prematurely
|
||||
|
||||
// override the port with the worker port temporarily
|
||||
process.env.PORT = WORKER_PORT
|
||||
const worker = require("../../worker/src/index")
|
||||
|
||||
// override the port with the server port
|
||||
process.env.PORT = SERVER_PORT
|
||||
const server = require("../src/app")
|
|
@ -45,8 +45,10 @@ async function initPro() {
|
|||
}
|
||||
|
||||
function shutdown(server?: any) {
|
||||
server.close()
|
||||
server.destroy()
|
||||
if (server) {
|
||||
server.close()
|
||||
server.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
export async function startup(app?: any, server?: any) {
|
||||
|
@ -69,39 +71,7 @@ export async function startup(app?: any, server?: any) {
|
|||
await migrations.migrate()
|
||||
} catch (e) {
|
||||
logging.logAlert("Error performing migrations. Exiting.", e)
|
||||
shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// check and create admin user if required
|
||||
if (
|
||||
env.SELF_HOSTED &&
|
||||
!env.MULTI_TENANCY &&
|
||||
env.BB_ADMIN_USER_EMAIL &&
|
||||
env.BB_ADMIN_USER_PASSWORD
|
||||
) {
|
||||
const checklist = await getChecklist()
|
||||
if (!checklist?.adminUser?.checked) {
|
||||
try {
|
||||
const tenantId = tenancy.getTenantId()
|
||||
const user = await createAdminUser(
|
||||
env.BB_ADMIN_USER_EMAIL,
|
||||
env.BB_ADMIN_USER_PASSWORD,
|
||||
tenantId
|
||||
)
|
||||
// Need to set up an API key for automated integration tests
|
||||
if (env.isTest()) {
|
||||
await generateApiKey(user._id)
|
||||
}
|
||||
|
||||
console.log(
|
||||
"Admin account automatically created for",
|
||||
env.BB_ADMIN_USER_EMAIL
|
||||
)
|
||||
} catch (e) {
|
||||
logging.logAlert("Error creating initial admin user. Exiting.", e)
|
||||
shutdown()
|
||||
}
|
||||
shutdown(server)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,4 +100,38 @@ export async function startup(app?: any, server?: any) {
|
|||
// bring routes online as final step once everything ready
|
||||
await initRoutes(app)
|
||||
}
|
||||
|
||||
// check and create admin user if required
|
||||
// this must be run after the api has been initialised due to
|
||||
// the app user sync
|
||||
if (
|
||||
env.SELF_HOSTED &&
|
||||
!env.MULTI_TENANCY &&
|
||||
env.BB_ADMIN_USER_EMAIL &&
|
||||
env.BB_ADMIN_USER_PASSWORD
|
||||
) {
|
||||
const checklist = await getChecklist()
|
||||
if (!checklist?.adminUser?.checked) {
|
||||
try {
|
||||
const tenantId = tenancy.getTenantId()
|
||||
const user = await createAdminUser(
|
||||
env.BB_ADMIN_USER_EMAIL,
|
||||
env.BB_ADMIN_USER_PASSWORD,
|
||||
tenantId
|
||||
)
|
||||
// Need to set up an API key for automated integration tests
|
||||
if (env.isTest()) {
|
||||
await generateApiKey(user._id)
|
||||
}
|
||||
|
||||
console.log(
|
||||
"Admin account automatically created for",
|
||||
env.BB_ADMIN_USER_EMAIL
|
||||
)
|
||||
} catch (e) {
|
||||
logging.logAlert("Error creating initial admin user. Exiting.", e)
|
||||
shutdown(server)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import { Hosting } from "../../sdk"
|
||||
|
||||
export interface CreateAccountRequest {
|
||||
email: string
|
||||
tenantId: string
|
||||
hosting: Hosting
|
||||
size: string
|
||||
profession: string
|
||||
// optional fields
|
||||
tenantName?: string
|
||||
name?: string
|
||||
password: string
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./accounts"
|
||||
export * from "./user"
|
||||
export * from "./license"
|
||||
export * from "./status"
|
||||
|
|
|
@ -3,6 +3,7 @@ export * from "./auth"
|
|||
export * from "./user"
|
||||
export * from "./errors"
|
||||
export * from "./schedule"
|
||||
export * from "./system"
|
||||
export * from "./app"
|
||||
export * from "./global"
|
||||
export * from "./pagination"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export interface GetEnvironmentResponse {
|
||||
multiTenancy: boolean
|
||||
cloud: boolean
|
||||
accountPortalUrl: string
|
||||
baseUrl: string
|
||||
disableAccountPortal: boolean
|
||||
isDev: boolean
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from "./environment"
|
|
@ -1,6 +1,7 @@
|
|||
import Nano from "@budibase/nano"
|
||||
import { AllDocsResponse, AnyDocument, Document } from "../"
|
||||
import { Writable } from "stream"
|
||||
import PouchDB from "pouchdb"
|
||||
|
||||
export enum SearchIndex {
|
||||
ROWS = "rows",
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"build:docker": "docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION",
|
||||
"dev:stack:init": "node ./scripts/dev/manage.js init",
|
||||
"dev:builder": "npm run dev:stack:init && nodemon",
|
||||
"dev:built": "yarn run dev:stack:init && yarn run run:docker",
|
||||
"test": "bash scripts/test.sh",
|
||||
"test:watch": "jest --watch",
|
||||
"env:multi:enable": "node scripts/multiTenancy.js enable",
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
BB_ADMIN_USER_EMAIL=qa@budibase.com
|
||||
BB_ADMIN_USER_PASSWORD=budibase
|
||||
COUCH_DB_URL=http://budibase:budibase@localhost:4567
|
||||
COUCH_DB_USER=budibase
|
||||
COUCH_DB_PASSWORD=budibase
|
||||
JWT_SECRET=test
|
||||
BUDIBASE_HOST=http://localhost:10000
|
||||
BUDIBASE_ACCOUNTS_URL=http://localhost:10001
|
|
@ -2,5 +2,4 @@ node_modules/
|
|||
.env
|
||||
watchtower-hook.json
|
||||
dist/
|
||||
.testReport.json
|
||||
testResults.json
|
||||
|
|
|
@ -6,13 +6,11 @@ The QA Core API tests are a jest suite that run directly against the budibase ba
|
|||
|
||||
You can run the whole test suite with one command, that spins up the budibase server and runs the jest tests:
|
||||
|
||||
`yarn api:test`
|
||||
`yarn test:ci`
|
||||
|
||||
## Setup Server Only
|
||||
## Setup Server
|
||||
|
||||
You can also just stand up the budibase server alone.
|
||||
|
||||
`yarn api:server:setup`
|
||||
You can run the local development stack by following the instructions on the main readme.
|
||||
|
||||
## Run Tests
|
||||
|
||||
|
@ -24,11 +22,7 @@ for watch mode, where the tests will run on every change:
|
|||
|
||||
`yarn test:watch`
|
||||
|
||||
To run tests locally against a cloud service you can use the command:
|
||||
`yarn run api:test:local`
|
||||
To run tests locally against a cloud service you can update the configuration inside the `.env` file and run:
|
||||
|
||||
To run the tests in CI, it assumes the correct environment variables are set, and the server is already running. Use the command:
|
||||
`yarn run api:test:ci`
|
||||
`yarn test`
|
||||
|
||||
To run the nightly tests against the QA environment, use the command:
|
||||
`yarn run api:test:nightly`
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
version: "3.8"
|
||||
services:
|
||||
qa-core-couchdb:
|
||||
# platform: linux/amd64
|
||||
container_name: budi-couchdb-qa
|
||||
restart: on-failure
|
||||
image: ibmcom/couchdb3
|
||||
environment:
|
||||
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
|
||||
- COUCHDB_USER=${COUCH_DB_USER}
|
||||
ports:
|
||||
- "4567:5984"
|
|
@ -0,0 +1,18 @@
|
|||
import { Config } from "@jest/types"
|
||||
|
||||
const config: Config.InitialOptions = {
|
||||
preset: "ts-jest",
|
||||
setupFiles: ["./src/jest/jestSetup.ts"],
|
||||
setupFilesAfterEnv: ["./src/jest/jest.extends.ts"],
|
||||
testEnvironment: "node",
|
||||
globalSetup: "./src/jest/globalSetup.ts",
|
||||
globalTeardown: "./src/jest/globalTeardown.ts",
|
||||
moduleNameMapper: {
|
||||
"@budibase/types": "<rootDir>/../packages/types/src",
|
||||
"@budibase/server": "<rootDir>/../packages/server/src",
|
||||
"@budibase/backend-core": "<rootDir>/../packages/backend-core/src",
|
||||
"@budibase/backend-core/(.*)": "<rootDir>/../packages/backend-core/$1",
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
|
@ -9,46 +9,26 @@
|
|||
"url": "https://github.com/Budibase/budibase.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "env-cmd jest --runInBand",
|
||||
"test:watch": "env-cmd jest --watch",
|
||||
"test:debug": "DEBUG=1 jest",
|
||||
"setup": "yarn && node scripts/createEnv.js",
|
||||
"test": "jest --runInBand --json --outputFile=testResults.json",
|
||||
"test:watch": "yarn run test --watch",
|
||||
"test:debug": "DEBUG=1 yarn run test",
|
||||
"test:notify": "node scripts/testResultsWebhook",
|
||||
"test:ci": "jest --runInBand --json --outputFile=testResults.json --testPathIgnorePatterns=\\\"\\/dataSources\\/\\\"",
|
||||
"docker:up": "docker-compose up -d",
|
||||
"docker:down": "docker-compose down",
|
||||
"api:server:setup": "npm run docker:up && env-cmd ts-node ../packages/builder/ts/setup.ts",
|
||||
"api:server:setup:ci": "env-cmd node ../packages/builder/setup.js",
|
||||
"api:test": "jest --runInBand --json --outputFile=testResults.json",
|
||||
"api:test:ci": "start-server-and-test api:server:setup:ci http://localhost:4100/builder test",
|
||||
"api:test:local": "start-server-and-test api:server:setup http://localhost:4100/builder test"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"testEnvironment": "node",
|
||||
"moduleNameMapper": {
|
||||
"@budibase/types": "<rootDir>/../packages/types/src",
|
||||
"@budibase/server": "<rootDir>/../packages/server/src",
|
||||
"@budibase/backend-core": "<rootDir>/../packages/backend-core/src",
|
||||
"@budibase/backend-core/(.*)": "<rootDir>/../packages/backend-core/$1"
|
||||
},
|
||||
"setupFiles": [
|
||||
"./scripts/jestSetup.js"
|
||||
],
|
||||
"setupFilesAfterEnv": [
|
||||
"./src/jest.extends.ts"
|
||||
]
|
||||
"test:smoke": "yarn run test --testPathIgnorePatterns=\\\"\\/dataSources\\/\\\"",
|
||||
"test:ci": "start-server-and-test dev:built http://localhost:4001/health test:smoke",
|
||||
"dev:built": "cd ../ && yarn dev:built"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@budibase/types": "^2.3.17",
|
||||
"@types/jest": "29.0.0",
|
||||
"@types/node-fetch": "2.6.2",
|
||||
"chance": "1.1.8",
|
||||
"env-cmd": "^10.1.0",
|
||||
"jest": "28.1.1",
|
||||
"dotenv": "16.0.1",
|
||||
"jest": "29.0.0",
|
||||
"prettier": "2.7.1",
|
||||
"start-server-and-test": "1.14.0",
|
||||
"timekeeper": "2.2.0",
|
||||
"ts-jest": "28.0.8",
|
||||
"ts-jest": "29.0.0",
|
||||
"ts-node": "10.8.1",
|
||||
"tsconfig-paths": "4.0.0",
|
||||
"typescript": "4.7.3"
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env node
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
|
||||
function init() {
|
||||
const envFilePath = path.join(process.cwd(), ".env")
|
||||
if (!fs.existsSync(envFilePath)) {
|
||||
const envFileJson = {
|
||||
BUDIBASE_URL: "http://localhost:10000",
|
||||
ACCOUNT_PORTAL_URL: "http://localhost:10001",
|
||||
BB_ADMIN_USER_EMAIL: "admin",
|
||||
BB_ADMIN_USER_PASSWORD: "admin",
|
||||
}
|
||||
let envFile = ""
|
||||
Object.keys(envFileJson).forEach(key => {
|
||||
envFile += `${key}=${envFileJson[key]}\n`
|
||||
})
|
||||
fs.writeFileSync(envFilePath, envFile)
|
||||
}
|
||||
}
|
||||
|
||||
init()
|
|
@ -1,11 +0,0 @@
|
|||
// mock all dates to 2020-01-01T00:00:00.000Z
|
||||
// use tk.reset() to use real dates in individual tests
|
||||
const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")
|
||||
const tk = require("timekeeper")
|
||||
tk.freeze(MOCK_DATE)
|
||||
|
||||
if (!process.env.DEBUG) {
|
||||
global.console.log = jest.fn() // console.log are ignored in tests
|
||||
}
|
||||
|
||||
jest.setTimeout(60000)
|
|
@ -0,0 +1,16 @@
|
|||
import AccountInternalAPIClient from "./AccountInternalAPIClient"
|
||||
import { AccountAPI, LicenseAPI } from "./apis"
|
||||
import { State } from "../../types"
|
||||
|
||||
export default class AccountInternalAPI {
|
||||
client: AccountInternalAPIClient
|
||||
|
||||
accounts: AccountAPI
|
||||
licenses: LicenseAPI
|
||||
|
||||
constructor(state: State) {
|
||||
this.client = new AccountInternalAPIClient(state)
|
||||
this.accounts = new AccountAPI(this.client)
|
||||
this.licenses = new LicenseAPI(this.client)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import env from "../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
import { State } from "../../types"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
export default class AccountInternalAPIClient {
|
||||
state: State
|
||||
host: string
|
||||
|
||||
constructor(state: State) {
|
||||
if (!env.ACCOUNT_PORTAL_URL) {
|
||||
throw new Error("Must set ACCOUNT_PORTAL_URL env var")
|
||||
}
|
||||
this.host = `${env.ACCOUNT_PORTAL_URL}`
|
||||
this.state = state
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
cookie: this.state.cookie,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
...options.headers,
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const response = await fetch(`${this.host}${url}`, requestOptions)
|
||||
|
||||
let body: any
|
||||
const contentType = response.headers.get("content-type")
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
body = await response.json()
|
||||
} else {
|
||||
body = await response.text()
|
||||
}
|
||||
|
||||
const message = `${method} ${url} - ${response.status}
|
||||
Response body: ${JSON.stringify(body)}
|
||||
Request body: ${requestOptions.body}`
|
||||
|
||||
if (response.status > 499) {
|
||||
console.error(message)
|
||||
} else if (response.status >= 400) {
|
||||
console.warn(message)
|
||||
}
|
||||
return [response, body]
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Account, CreateAccountRequest } from "@budibase/types"
|
||||
import AccountInternalAPIClient from "../AccountInternalAPIClient"
|
||||
import { APIRequestOpts } from "../../../types"
|
||||
|
||||
export default class AccountAPI {
|
||||
client: AccountInternalAPIClient
|
||||
|
||||
constructor(client: AccountInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async validateEmail(
|
||||
email: string,
|
||||
opts: APIRequestOpts = { doExpect: true }
|
||||
): Promise<Response> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/api/accounts/validate/email`,
|
||||
{
|
||||
body: { email },
|
||||
}
|
||||
)
|
||||
if (opts.doExpect) {
|
||||
expect(response).toHaveStatusCode(200)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
async validateTenantId(
|
||||
tenantId: string,
|
||||
opts: APIRequestOpts = { doExpect: true }
|
||||
): Promise<Response> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/api/accounts/validate/tenantId`,
|
||||
{
|
||||
body: { tenantId },
|
||||
}
|
||||
)
|
||||
if (opts.doExpect) {
|
||||
expect(response).toHaveStatusCode(200)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
async create(
|
||||
body: CreateAccountRequest,
|
||||
opts: APIRequestOpts = { doExpect: true }
|
||||
): Promise<[Response, Account]> {
|
||||
const headers = {
|
||||
"no-verify": "1",
|
||||
}
|
||||
const [response, json] = await this.client.post(`/api/accounts`, {
|
||||
body,
|
||||
headers,
|
||||
})
|
||||
if (opts.doExpect) {
|
||||
expect(response).toHaveStatusCode(201)
|
||||
}
|
||||
return [response, json]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import AccountInternalAPIClient from "../AccountInternalAPIClient"
|
||||
|
||||
export default class LicenseAPI {
|
||||
client: AccountInternalAPIClient
|
||||
|
||||
constructor(client: AccountInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export { default as AccountAPI } from "./AccountAPI"
|
||||
export { default as LicenseAPI } from "./LicenseAPI"
|
|
@ -0,0 +1 @@
|
|||
export { default as AccountInternalAPI } from "./AccountInternalAPI"
|
|
@ -0,0 +1 @@
|
|||
export * from "./api"
|
|
@ -1,73 +0,0 @@
|
|||
import env from "../../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
class InternalAPIClient {
|
||||
host: string
|
||||
tenantName?: string
|
||||
appId?: string
|
||||
cookie?: string
|
||||
|
||||
constructor(appId?: string) {
|
||||
if (!env.BUDIBASE_HOST) {
|
||||
throw new Error("Must set BUDIBASE_HOST env var")
|
||||
}
|
||||
this.host = `${env.BUDIBASE_HOST}/api`
|
||||
this.appId = appId
|
||||
}
|
||||
|
||||
setTenantName(tenantName: string) {
|
||||
this.tenantName = tenantName
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"x-budibase-app-id": this.appId,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
cookie: this.cookie,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
...options.headers,
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
// @ts-ignore
|
||||
const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions)
|
||||
|
||||
if (
|
||||
response.status == 404 ||
|
||||
response.status == 500 ||
|
||||
response.status == 403
|
||||
) {
|
||||
console.error("Error in apiCall")
|
||||
console.error("Response:", response)
|
||||
const json = await response.json()
|
||||
console.error("Response body:", json)
|
||||
console.error("Request body:", requestOptions.body)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
||||
|
||||
export default InternalAPIClient
|
|
@ -1,38 +0,0 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Account } from "@budibase/types"
|
||||
import AccountsAPIClient from "./accountsAPIClient"
|
||||
import { NewAccount } from "../fixtures/types/newAccount"
|
||||
|
||||
export default class AccountsApi {
|
||||
api: AccountsAPIClient
|
||||
|
||||
constructor(AccountsAPIClient: AccountsAPIClient) {
|
||||
this.api = AccountsAPIClient
|
||||
}
|
||||
|
||||
async validateEmail(email: string): Promise<Response> {
|
||||
const response = await this.api.post(`/accounts/validate/email`, {
|
||||
body: { email },
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return response
|
||||
}
|
||||
|
||||
async validateTenantId(tenantId: string): Promise<Response> {
|
||||
const response = await this.api.post(`/accounts/validate/tenantId`, {
|
||||
body: { tenantId },
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return response
|
||||
}
|
||||
|
||||
async create(body: Partial<NewAccount>): Promise<[Response, Account]> {
|
||||
const headers = {
|
||||
"no-verify": "1",
|
||||
}
|
||||
const response = await this.api.post(`/accounts`, { body, headers })
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(201)
|
||||
return [response, json]
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
import env from "../../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
class AccountsAPIClient {
|
||||
host: string
|
||||
appId?: string
|
||||
cookie?: string
|
||||
|
||||
constructor(appId?: string) {
|
||||
if (!env.BUDIBASE_ACCOUNTS_URL) {
|
||||
throw new Error("Must set BUDIBASE_ACCOUNTS_URL env var")
|
||||
}
|
||||
this.host = `${env.BUDIBASE_ACCOUNTS_URL}/api`
|
||||
this.appId = appId
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"x-budibase-app-id": this.appId,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
cookie: this.cookie,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
...options.headers,
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const response = await fetch(`${this.host}${url}`, requestOptions)
|
||||
if (response.status == 404 || response.status == 500) {
|
||||
console.error("Error in apiCall")
|
||||
console.error("Response:", response)
|
||||
const json = await response.json()
|
||||
console.error("Response body:", json)
|
||||
console.error("Request body:", requestOptions.body)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
||||
|
||||
export default AccountsAPIClient
|
|
@ -1,178 +0,0 @@
|
|||
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { App } from "@budibase/types"
|
||||
import { Response } from "node-fetch"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
import { RouteConfig } from "../fixtures/types/routing"
|
||||
import { AppPackageResponse } from "../fixtures/types/appPackage"
|
||||
import { DeployConfig } from "../fixtures/types/deploy"
|
||||
import { responseMessage } from "../fixtures/types/responseMessage"
|
||||
import { UnpublishAppResponse } from "../fixtures/types/unpublishAppResponse"
|
||||
|
||||
export default class AppApi {
|
||||
api: InternalAPIClient
|
||||
constructor(apiClient: InternalAPIClient) {
|
||||
this.api = apiClient
|
||||
}
|
||||
// TODO Fix the fetch apps to receive an optional number of apps and compare if the received app is more or less.
|
||||
// each possible scenario should have its own method.
|
||||
async fetchEmptyAppList(): Promise<[Response, Application[]]> {
|
||||
const response = await this.api.get(`/applications?status=all`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBeGreaterThanOrEqual(0)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async fetchAllApplications(): Promise<[Response, Application[]]> {
|
||||
const response = await this.api.get(`/applications?status=all`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBeGreaterThanOrEqual(1)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async canRender(): Promise<[Response, boolean]> {
|
||||
const response = await this.api.get("/routing/client")
|
||||
expect(response).toHaveStatusCode(200)
|
||||
const json = await response.json()
|
||||
const publishedAppRenders = Object.keys(json.routes).length > 0
|
||||
expect(publishedAppRenders).toBe(true)
|
||||
return [response, publishedAppRenders]
|
||||
}
|
||||
|
||||
async getAppPackage(appId: string): Promise<[Response, AppPackageResponse]> {
|
||||
const response = await this.api.get(`/applications/${appId}/appPackage`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.application.appId).toEqual(appId)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async publish(appId: string | undefined): Promise<[Response, DeployConfig]> {
|
||||
const response = await this.api.post(`/applications/${appId}/publish`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async create(body: any): Promise<Partial<App>> {
|
||||
const response = await this.api.post(`/applications`, { body })
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toBeDefined()
|
||||
return json
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, Application]> {
|
||||
const response = await this.api.get(`/applications/${id}`)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async sync(appId: string): Promise<[Response, responseMessage]> {
|
||||
const response = await this.api.post(`/applications/${appId}/sync`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
// TODO
|
||||
async updateClient(
|
||||
appId: string,
|
||||
body: any
|
||||
): Promise<[Response, Application]> {
|
||||
const response = await this.api.put(
|
||||
`/applications/${appId}/client/update`,
|
||||
{ body }
|
||||
)
|
||||
const json = await response.json()
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async revertPublished(appId: string): Promise<[Response, responseMessage]> {
|
||||
const response = await this.api.post(`/dev/${appId}/revert`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json).toEqual({
|
||||
message: "Reverted changes successfully.",
|
||||
})
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async revertUnpublished(appId: string): Promise<[Response, responseMessage]> {
|
||||
const response = await this.api.post(`/dev/${appId}/revert`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(400)
|
||||
expect(json).toEqual({
|
||||
message: "App has not yet been deployed",
|
||||
status: 400,
|
||||
})
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async delete(appId: string): Promise<Response> {
|
||||
const response = await this.api.del(`/applications/${appId}`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return response
|
||||
}
|
||||
|
||||
async rename(
|
||||
appId: string,
|
||||
oldName: string,
|
||||
body: any
|
||||
): Promise<[Response, Application]> {
|
||||
const response = await this.api.put(`/applications/${appId}`, { body })
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.name).not.toEqual(oldName)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async addScreentoApp(body: any): Promise<[Response, Application]> {
|
||||
const response = await this.api.post(`/screens`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async getRoutes(screenExists?: boolean): Promise<[Response, RouteConfig]> {
|
||||
const response = await this.api.get(`/routing`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
if (screenExists) {
|
||||
expect(json.routes["/test"]).toBeTruthy()
|
||||
} else {
|
||||
expect(json.routes["/test"]).toBeUndefined()
|
||||
}
|
||||
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async unpublish(appId: string): Promise<[Response]> {
|
||||
const response = await this.api.post(`/applications/${appId}/unpublish`)
|
||||
expect(response).toHaveStatusCode(204)
|
||||
return [response]
|
||||
}
|
||||
|
||||
async unlock(appId: string): Promise<[Response, responseMessage]> {
|
||||
const response = await this.api.del(`/dev/${appId}/lock`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.message).toEqual("Lock released successfully.")
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async updateIcon(appId: string): Promise<[Response, Application]> {
|
||||
const body = {
|
||||
icon: {
|
||||
name: "ConversionFunnel",
|
||||
color: "var(--spectrum-global-color-red-400)",
|
||||
},
|
||||
}
|
||||
const response = await this.api.put(`/applications/${appId}`, { body })
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.icon.name).toEqual(body.icon.name)
|
||||
expect(json.icon.color).toEqual(body.icon.color)
|
||||
return [response, json]
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import { Response } from "node-fetch"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
|
||||
export default class AuthApi {
|
||||
api: InternalAPIClient
|
||||
|
||||
constructor(apiClient: InternalAPIClient) {
|
||||
this.api = apiClient
|
||||
}
|
||||
|
||||
async loginAsAdmin(): Promise<[Response, any]> {
|
||||
const response = await this.api.post(`/global/auth/default/login`, {
|
||||
body: {
|
||||
username: process.env.BB_ADMIN_USER_EMAIL,
|
||||
password: process.env.BB_ADMIN_USER_PASSWORD,
|
||||
},
|
||||
})
|
||||
const cookie = response.headers.get("set-cookie")
|
||||
this.api.cookie = cookie as any
|
||||
return [response, cookie]
|
||||
}
|
||||
|
||||
async login(email: String, password: String): Promise<[Response, any]> {
|
||||
const response = await this.api.post(`/global/auth/default/login`, {
|
||||
body: {
|
||||
username: email,
|
||||
password: password,
|
||||
},
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
const cookie = response.headers.get("set-cookie")
|
||||
this.api.cookie = cookie as any
|
||||
return [response, cookie]
|
||||
}
|
||||
|
||||
async logout(): Promise<any> {
|
||||
return this.api.post(`/global/auth/logout`)
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
const Chance = require("chance")
|
||||
|
||||
export default new Chance()
|
|
@ -1,79 +0,0 @@
|
|||
import ApplicationApi from "./applications"
|
||||
import AuthApi from "./auth"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
import AccountsApiClient from "./accountsAPIClient"
|
||||
import TablesApi from "./tables"
|
||||
import RowApi from "./rows"
|
||||
import ScreenApi from "./screens"
|
||||
import UserManagementApi from "./userManagement"
|
||||
import AccountsApi from "./accounts"
|
||||
import { generateAccount } from "../fixtures/accounts"
|
||||
|
||||
export default class TestConfiguration<T> {
|
||||
applications: ApplicationApi
|
||||
auth: AuthApi
|
||||
screen: ScreenApi
|
||||
context: T
|
||||
tables: TablesApi
|
||||
rows: RowApi
|
||||
users: UserManagementApi
|
||||
accounts: AccountsApi
|
||||
apiClient: InternalAPIClient
|
||||
accountsApiClient: AccountsApiClient
|
||||
|
||||
constructor(
|
||||
apiClient: InternalAPIClient,
|
||||
accountsApiClient: AccountsApiClient
|
||||
) {
|
||||
this.apiClient = apiClient
|
||||
this.accountsApiClient = accountsApiClient
|
||||
|
||||
this.applications = new ApplicationApi(this.apiClient)
|
||||
this.tables = new TablesApi(this.apiClient)
|
||||
this.rows = new RowApi(this.apiClient)
|
||||
this.auth = new AuthApi(this.apiClient)
|
||||
this.screen = new ScreenApi(this.apiClient)
|
||||
this.users = new UserManagementApi(this.apiClient)
|
||||
this.accounts = new AccountsApi(this.accountsApiClient)
|
||||
this.context = <T>{}
|
||||
}
|
||||
|
||||
async loginAsAdmin() {
|
||||
await this.auth.login(
|
||||
<string>process.env.BB_ADMIN_USER_EMAIL,
|
||||
<string>process.env.BB_ADMIN_USER_PASSWORD
|
||||
)
|
||||
}
|
||||
// TODO: add logic to setup or login based in env variables
|
||||
|
||||
async setupAccountAndTenant() {
|
||||
const account = generateAccount()
|
||||
await this.accounts.validateEmail(<string>account.email)
|
||||
await this.accounts.validateTenantId(<string>account.tenantId)
|
||||
process.env.TENANT_ID = <string>account.tenantId
|
||||
await this.accounts.create(account)
|
||||
await this.updateApiClients(<string>account.tenantName)
|
||||
await this.auth.login(<string>account.email, <string>account.password)
|
||||
}
|
||||
|
||||
async updateApiClients(tenantName: string) {
|
||||
this.apiClient.setTenantName(tenantName)
|
||||
this.applications = new ApplicationApi(this.apiClient)
|
||||
this.tables = new TablesApi(this.apiClient)
|
||||
this.rows = new RowApi(this.apiClient)
|
||||
this.auth = new AuthApi(this.apiClient)
|
||||
this.screen = new ScreenApi(this.apiClient)
|
||||
this.users = new UserManagementApi(this.apiClient)
|
||||
this.context = <T>{}
|
||||
}
|
||||
|
||||
async login(email: string, password: string) {
|
||||
await this.auth.logout()
|
||||
await this.auth.login(email, password)
|
||||
}
|
||||
|
||||
async afterAll() {
|
||||
this.context = <T>{}
|
||||
await this.auth.logout()
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export interface responseMessage {
|
||||
message: string
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
import env from "../../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
class PublicAPIClient {
|
||||
host: string
|
||||
apiKey?: string
|
||||
tenantName?: string
|
||||
appId?: string
|
||||
cookie?: string
|
||||
|
||||
constructor(appId?: string) {
|
||||
if (!env.BUDIBASE_HOST) {
|
||||
throw new Error("Must set BUDIBASE_HOST env var")
|
||||
}
|
||||
this.host = `${env.BUDIBASE_HOST}/api/public/v1`
|
||||
|
||||
this.appId = appId
|
||||
}
|
||||
|
||||
setTenantName(tenantName: string) {
|
||||
this.tenantName = tenantName
|
||||
}
|
||||
|
||||
setApiKey(apiKey: string) {
|
||||
this.apiKey = apiKey
|
||||
process.env.BUDIBASE_PUBLIC_API_KEY = apiKey
|
||||
this.host = `${env.BUDIBASE_HOST}/api/public/v1`
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"x-budibase-api-key": this.apiKey || null,
|
||||
"x-budibase-app-id": this.appId,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
...options.headers,
|
||||
cookie: this.cookie,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
},
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
// @ts-ignore
|
||||
const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions)
|
||||
|
||||
if (response.status == 500 || response.status == 403) {
|
||||
console.error("Error in apiCall")
|
||||
console.error("Response:", response)
|
||||
const json = await response.json()
|
||||
console.error("Response body:", json)
|
||||
console.error("Request body:", requestOptions.body)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
||||
|
||||
export default PublicAPIClient
|
|
@ -1,38 +0,0 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Account } from "@budibase/types"
|
||||
import AccountsAPIClient from "./accountsAPIClient"
|
||||
import { NewAccount } from "../fixtures/types/newAccount"
|
||||
|
||||
export default class AccountsApi {
|
||||
api: AccountsAPIClient
|
||||
|
||||
constructor(AccountsAPIClient: AccountsAPIClient) {
|
||||
this.api = AccountsAPIClient
|
||||
}
|
||||
|
||||
async validateEmail(email: string): Promise<Response> {
|
||||
const response = await this.api.post(`/accounts/validate/email`, {
|
||||
body: { email },
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return response
|
||||
}
|
||||
|
||||
async validateTenantId(tenantId: string): Promise<Response> {
|
||||
const response = await this.api.post(`/accounts/validate/tenantId`, {
|
||||
body: { tenantId },
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return response
|
||||
}
|
||||
|
||||
async create(body: Partial<NewAccount>): Promise<[Response, Account]> {
|
||||
const headers = {
|
||||
"no-verify": "1",
|
||||
}
|
||||
const response = await this.api.post(`/accounts`, { body, headers })
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(201)
|
||||
return [response, json]
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
import env from "../../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
class AccountsAPIClient {
|
||||
host: string
|
||||
appId?: string
|
||||
cookie?: string
|
||||
|
||||
constructor(appId?: string) {
|
||||
if (!env.BUDIBASE_ACCOUNTS_URL) {
|
||||
throw new Error("Must set BUDIBASE_ACCOUNTS_URL env var")
|
||||
}
|
||||
this.host = `${env.BUDIBASE_ACCOUNTS_URL}/api`
|
||||
this.appId = appId
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"x-budibase-app-id": this.appId,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
cookie: this.cookie,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
...options.headers,
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const response = await fetch(`${this.host}${url}`, requestOptions)
|
||||
if (
|
||||
response.status == 404 ||
|
||||
response.status == 500 ||
|
||||
response.status == 400
|
||||
) {
|
||||
console.error("Error in apiCall")
|
||||
console.error("Response:", response)
|
||||
const json = await response.json()
|
||||
console.error("Response body:", json)
|
||||
console.error("Request body:", requestOptions.body)
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
||||
|
||||
export default AccountsAPIClient
|
|
@ -1,77 +0,0 @@
|
|||
import PublicAPIClient from "./PublicAPIClient"
|
||||
import {
|
||||
Application,
|
||||
SearchInputParams,
|
||||
CreateApplicationParams,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { Response } from "node-fetch"
|
||||
import generateApp from "../fixtures/applications"
|
||||
|
||||
export default class AppApi {
|
||||
api: PublicAPIClient
|
||||
|
||||
constructor(apiClient: PublicAPIClient) {
|
||||
this.api = apiClient
|
||||
}
|
||||
|
||||
async seed(): Promise<[Response, Application]> {
|
||||
return this.create(generateApp())
|
||||
}
|
||||
|
||||
async create(
|
||||
body: CreateApplicationParams
|
||||
): Promise<[Response, Application]> {
|
||||
const response = await this.api.post(`/applications`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, Application]> {
|
||||
const response = await this.api.get(`/applications/${id}`)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [Application]]> {
|
||||
const response = await this.api.post(`/applications/search`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
body: Application
|
||||
): Promise<[Response, Application]> {
|
||||
const response = await this.api.put(`/applications/${id}`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<[Response, Application]> {
|
||||
const response = await this.api.del(`/applications/${id}`)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async publish(id: string): Promise<[Response, any]> {
|
||||
const response = await this.api.post(`/applications/${id}/publish`)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async unpublish(id: string): Promise<[Response]> {
|
||||
const response = await this.api.post(`/applications/${id}/unpublish`)
|
||||
return [response]
|
||||
}
|
||||
|
||||
async createFirstApp() {
|
||||
const body = {
|
||||
name: "My first app",
|
||||
url: "my-first-app",
|
||||
useTemplate: false,
|
||||
sampleData: true,
|
||||
}
|
||||
const response = await this.api.post("/applications", { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import { Response } from "node-fetch"
|
||||
import AccountsAPIClient from "./accountsAPIClient"
|
||||
import { ApiKeyResponse } from "../fixtures/types/apiKeyResponse"
|
||||
|
||||
export default class AuthApi {
|
||||
api: AccountsAPIClient
|
||||
|
||||
constructor(apiClient: AccountsAPIClient) {
|
||||
this.api = apiClient
|
||||
}
|
||||
|
||||
async loginAsAdmin(): Promise<[Response, any]> {
|
||||
const response = await this.api.post(`/auth/login`, {
|
||||
body: {
|
||||
username: process.env.BB_ADMIN_USER_EMAIL,
|
||||
password: process.env.BB_ADMIN_USER_PASSWORD,
|
||||
},
|
||||
})
|
||||
const cookie = response.headers.get("set-cookie")
|
||||
this.api.cookie = cookie as any
|
||||
return [response, cookie]
|
||||
}
|
||||
|
||||
async login(email: String, password: String): Promise<[Response, any]> {
|
||||
const response = await this.api.post(`/global/auth/default/login`, {
|
||||
body: {
|
||||
username: email,
|
||||
password: password,
|
||||
},
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
const cookie = response.headers.get("set-cookie")
|
||||
this.api.cookie = cookie as any
|
||||
return [response, cookie]
|
||||
}
|
||||
|
||||
async logout(): Promise<any> {
|
||||
return this.api.post(`/global/auth/logout`)
|
||||
}
|
||||
|
||||
async getApiKey(): Promise<ApiKeyResponse> {
|
||||
const response = await this.api.get(`/global/self/api_key`)
|
||||
const json = await response.json()
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json).toHaveProperty("apiKey")
|
||||
return json
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
import PublicAPIClient from "./PublicAPIClient"
|
||||
import ApplicationApi from "./applications"
|
||||
import TableApi from "./tables"
|
||||
import UserApi from "./users"
|
||||
import RowApi from "./rows"
|
||||
import AuthApi from "./auth"
|
||||
import AccountsApiClient from "./accountsAPIClient"
|
||||
import AccountsApi from "./accounts"
|
||||
import { generateAccount } from "../fixtures/accounts"
|
||||
import internalApplicationsApi from "../../internal-api/TestConfiguration/applications"
|
||||
|
||||
import InternalAPIClient from "../../internal-api/TestConfiguration/InternalAPIClient"
|
||||
|
||||
export default class TestConfiguration<T> {
|
||||
applications: ApplicationApi
|
||||
auth: AuthApi
|
||||
users: UserApi
|
||||
tables: TableApi
|
||||
rows: RowApi
|
||||
context: T
|
||||
accounts: AccountsApi
|
||||
apiClient: PublicAPIClient
|
||||
accountsApiClient: AccountsApiClient
|
||||
internalApiClient: InternalAPIClient
|
||||
internalApplicationsApi: internalApplicationsApi
|
||||
|
||||
constructor(
|
||||
apiClient: PublicAPIClient,
|
||||
accountsApiClient: AccountsApiClient,
|
||||
internalApiClient: InternalAPIClient
|
||||
) {
|
||||
this.apiClient = apiClient
|
||||
this.accountsApiClient = accountsApiClient
|
||||
this.internalApiClient = internalApiClient
|
||||
|
||||
this.auth = new AuthApi(this.internalApiClient)
|
||||
this.accounts = new AccountsApi(this.accountsApiClient)
|
||||
this.applications = new ApplicationApi(apiClient)
|
||||
this.users = new UserApi(apiClient)
|
||||
this.tables = new TableApi(apiClient)
|
||||
this.rows = new RowApi(apiClient)
|
||||
this.internalApplicationsApi = new internalApplicationsApi(
|
||||
internalApiClient
|
||||
)
|
||||
|
||||
this.context = <T>{}
|
||||
}
|
||||
|
||||
async setupAccountAndTenant() {
|
||||
// This step is required to create a new account and tenant for the tests, its part of
|
||||
// the support for running tests in multiple environments.
|
||||
const account = generateAccount()
|
||||
await this.accounts.validateEmail(<string>account.email)
|
||||
await this.accounts.validateTenantId(<string>account.tenantId)
|
||||
process.env.TENANT_ID = <string>account.tenantId
|
||||
await this.accounts.create(account)
|
||||
await this.updateApiClients(<string>account.tenantName)
|
||||
await this.auth.login(<string>account.email, <string>account.password)
|
||||
const body = {
|
||||
name: "My first app",
|
||||
url: "my-first-app",
|
||||
useTemplate: false,
|
||||
sampleData: true,
|
||||
}
|
||||
await this.internalApplicationsApi.create(body)
|
||||
}
|
||||
|
||||
// After the account and tenant have been created, we need to get and set the API key for the test
|
||||
async setApiKey() {
|
||||
const apiKeyResponse = await this.auth.getApiKey()
|
||||
this.apiClient.setApiKey(apiKeyResponse.apiKey)
|
||||
}
|
||||
async updateApiClients(tenantName: string) {
|
||||
this.apiClient.setTenantName(tenantName)
|
||||
this.applications = new ApplicationApi(this.apiClient)
|
||||
this.rows = new RowApi(this.apiClient)
|
||||
this.internalApiClient.setTenantName(tenantName)
|
||||
this.internalApplicationsApi = new internalApplicationsApi(
|
||||
this.internalApiClient
|
||||
)
|
||||
this.auth = new AuthApi(this.internalApiClient)
|
||||
this.context = <T>{}
|
||||
}
|
||||
|
||||
async beforeAll() {}
|
||||
|
||||
async afterAll() {
|
||||
this.context = <T>{}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import PublicAPIClient from "./PublicAPIClient"
|
||||
import {
|
||||
CreateRowParams,
|
||||
Row,
|
||||
SearchInputParams,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { HeadersInit, Response } from "node-fetch"
|
||||
import { generateRow } from "../fixtures/tables"
|
||||
|
||||
export default class RowApi {
|
||||
api: PublicAPIClient
|
||||
headers?: HeadersInit
|
||||
tableId?: string
|
||||
|
||||
constructor(apiClient: PublicAPIClient) {
|
||||
this.api = apiClient
|
||||
}
|
||||
|
||||
async seed(tableId: string) {
|
||||
return this.create(generateRow({ tableId }))
|
||||
}
|
||||
|
||||
async create(body: CreateRowParams): Promise<[Response, Row]> {
|
||||
const response = await this.api.post(`/tables/${this.tableId}/rows`, {
|
||||
body,
|
||||
})
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, Row]> {
|
||||
const response = await this.api.get(`/tables/${this.tableId}/rows/${id}`)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [Row]]> {
|
||||
const response = await this.api.post(
|
||||
`/tables/${this.tableId}/rows/search`,
|
||||
{ body }
|
||||
)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(id: string, body: Row): Promise<[Response, Row]> {
|
||||
const response = await this.api.put(`/tables/${this.tableId}/rows/${id}`, {
|
||||
body,
|
||||
})
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import PublicAPIClient from "./PublicAPIClient"
|
||||
import {
|
||||
CreateUserParams,
|
||||
SearchInputParams,
|
||||
User,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { Response } from "node-fetch"
|
||||
import generateUser from "../fixtures/users"
|
||||
|
||||
export default class UserApi {
|
||||
api: PublicAPIClient
|
||||
|
||||
constructor(apiClient: PublicAPIClient) {
|
||||
this.api = apiClient
|
||||
}
|
||||
|
||||
async seed() {
|
||||
return this.create(generateUser())
|
||||
}
|
||||
|
||||
async create(body: CreateUserParams): Promise<[Response, User]> {
|
||||
const response = await this.api.post(`/users`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, User]> {
|
||||
const response = await this.api.get(`/users/${id}`)
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [User]]> {
|
||||
const response = await this.api.post(`/users/search`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(id: string, body: User): Promise<[Response, User]> {
|
||||
const response = await this.api.put(`/users/${id}`, { body })
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import { NewAccount } from "./types/newAccount"
|
||||
|
||||
import generator from "../../generator"
|
||||
import { Hosting } from "@budibase/types"
|
||||
|
||||
export const generateAccount = (): Partial<NewAccount> => {
|
||||
const randomGuid = generator.guid()
|
||||
//Needs to start with a letter
|
||||
let tenant: string = "tenant" + randomGuid
|
||||
tenant = tenant.replace(/-/g, "")
|
||||
|
||||
return {
|
||||
email: `qa+${randomGuid}@budibase.com`,
|
||||
hosting: Hosting.CLOUD,
|
||||
name: `qa+${randomGuid}@budibase.com`,
|
||||
password: `${randomGuid}`,
|
||||
profession: "software_engineer",
|
||||
size: "10+",
|
||||
tenantId: `${tenant}`,
|
||||
tenantName: `${tenant}`,
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import generator from "../../generator"
|
||||
import {
|
||||
Application,
|
||||
CreateApplicationParams,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
|
||||
const generate = (
|
||||
overrides: Partial<Application> = {}
|
||||
): CreateApplicationParams => ({
|
||||
name: generator.word(),
|
||||
url: `/${generator.word()}`,
|
||||
...overrides,
|
||||
})
|
||||
|
||||
export default generate
|
|
@ -1,5 +0,0 @@
|
|||
import { Account } from "@budibase/types"
|
||||
|
||||
export interface NewAccount extends Account {
|
||||
password: string
|
||||
}
|
|
@ -1,12 +1,18 @@
|
|||
import { join } from "path"
|
||||
|
||||
let LOADED = false
|
||||
if (!LOADED) {
|
||||
require("dotenv").config({
|
||||
path: join(__dirname, "..", ".env"),
|
||||
})
|
||||
LOADED = true
|
||||
}
|
||||
|
||||
const env = {
|
||||
BUDIBASE_ACCOUNT_URL: process.env.BUDIBASE_ACCOUNT_URL,
|
||||
BUDIBASE_PUBLIC_API_KEY: process.env.BUDIBASE_PUBLIC_API_KEY,
|
||||
BUDIBASE_ACCOUNTS_URL: process.env.BUDIBASE_ACCOUNTS_URL,
|
||||
BUDIBASE_HOST: process.env.BUDIBASE_HOST,
|
||||
_set(key: any, value: any) {
|
||||
process.env[key] = value
|
||||
module.exports[key] = value
|
||||
},
|
||||
BUDIBASE_URL: process.env.BUDIBASE_URL,
|
||||
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
|
||||
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
|
||||
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
|
||||
}
|
||||
|
||||
export = env
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import AppAPI from "./apis/AppAPI"
|
||||
import AuthAPI from "./apis/AuthAPI"
|
||||
import EnvironmentAPI from "./apis/EnvironmentAPI"
|
||||
import RoleAPI from "./apis/RoleAPI"
|
||||
import RowAPI from "./apis/RowAPI"
|
||||
import ScreenAPI from "./apis/ScreenAPI"
|
||||
import SelfAPI from "./apis/SelfAPI"
|
||||
import TableAPI from "./apis/TableAPI"
|
||||
import UserAPI from "./apis/UserAPI"
|
||||
import BudibaseInternalAPIClient from "./BudibaseInternalAPIClient"
|
||||
import { State } from "../../types"
|
||||
|
||||
export default class BudibaseInternalAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
apps: AppAPI
|
||||
auth: AuthAPI
|
||||
environment: EnvironmentAPI
|
||||
roles: RoleAPI
|
||||
rows: RowAPI
|
||||
screens: ScreenAPI
|
||||
self: SelfAPI
|
||||
tables: TableAPI
|
||||
users: UserAPI
|
||||
|
||||
constructor(state: State) {
|
||||
this.client = new BudibaseInternalAPIClient(state)
|
||||
|
||||
this.apps = new AppAPI(this.client)
|
||||
this.auth = new AuthAPI(this.client, state)
|
||||
this.environment = new EnvironmentAPI(this.client)
|
||||
this.roles = new RoleAPI(this.client)
|
||||
this.rows = new RowAPI(this.client)
|
||||
this.screens = new ScreenAPI(this.client)
|
||||
this.self = new SelfAPI(this.client)
|
||||
this.tables = new TableAPI(this.client)
|
||||
this.users = new UserAPI(this.client)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import env from "../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
import { State } from "../../types"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
class BudibaseInternalAPIClient {
|
||||
host: string
|
||||
state: State
|
||||
|
||||
constructor(state: State) {
|
||||
if (!env.BUDIBASE_URL) {
|
||||
throw new Error("Must set BUDIBASE_URL env var")
|
||||
}
|
||||
this.host = `${env.ACCOUNT_PORTAL_URL}/api`
|
||||
this.host = `${env.BUDIBASE_URL}/api`
|
||||
this.state = state
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"x-budibase-app-id": this.state.appId,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
cookie: this.state.cookie,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
...options.headers,
|
||||
},
|
||||
credentials: "include",
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
// @ts-ignore
|
||||
const response = await fetch(`${this.host}${url}`, requestOptions)
|
||||
|
||||
let body: any
|
||||
const contentType = response.headers.get("content-type")
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
body = await response.json()
|
||||
} else {
|
||||
body = await response.text()
|
||||
}
|
||||
|
||||
const message = `${method} ${url} - ${response.status}
|
||||
Response body: ${JSON.stringify(body)}
|
||||
Request body: ${requestOptions.body}`
|
||||
|
||||
if (response.status > 499) {
|
||||
console.error(message)
|
||||
} else if (response.status >= 400) {
|
||||
console.warn(message)
|
||||
}
|
||||
|
||||
return [response, body]
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
||||
|
||||
export default BudibaseInternalAPIClient
|
|
@ -0,0 +1,173 @@
|
|||
import { App } from "@budibase/types"
|
||||
import { Response } from "node-fetch"
|
||||
import {
|
||||
RouteConfig,
|
||||
AppPackageResponse,
|
||||
DeployConfig,
|
||||
MessageResponse,
|
||||
} from "../../../types"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
|
||||
export default class AppAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
// TODO Fix the fetch apps to receive an optional number of apps and compare if the received app is more or less.
|
||||
// each possible scenario should have its own method.
|
||||
async fetchEmptyAppList(): Promise<[Response, App[]]> {
|
||||
const [response, json] = await this.client.get(`/applications?status=all`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBeGreaterThanOrEqual(0)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async fetchAllApplications(): Promise<[Response, App[]]> {
|
||||
const [response, json] = await this.client.get(`/applications?status=all`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBeGreaterThanOrEqual(1)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async canRender(): Promise<[Response, boolean]> {
|
||||
const [response, json] = await this.client.get("/routing/client")
|
||||
expect(response).toHaveStatusCode(200)
|
||||
const publishedAppRenders = Object.keys(json.routes).length > 0
|
||||
expect(publishedAppRenders).toBe(true)
|
||||
return [response, publishedAppRenders]
|
||||
}
|
||||
|
||||
async getAppPackage(appId: string): Promise<[Response, AppPackageResponse]> {
|
||||
const [response, json] = await this.client.get(
|
||||
`/applications/${appId}/appPackage`
|
||||
)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.application.appId).toEqual(appId)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async publish(appId: string | undefined): Promise<[Response, DeployConfig]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/applications/${appId}/publish`
|
||||
)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async create(body: any): Promise<App> {
|
||||
const [response, json] = await this.client.post(`/applications`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toBeDefined()
|
||||
return json
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, App]> {
|
||||
const [response, json] = await this.client.get(`/applications/${id}`)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async sync(appId: string): Promise<[Response, MessageResponse]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/applications/${appId}/sync`
|
||||
)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
// TODO
|
||||
async updateClient(appId: string, body: any): Promise<[Response, App]> {
|
||||
const [response, json] = await this.client.put(
|
||||
`/applications/${appId}/client/update`,
|
||||
{ body }
|
||||
)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async revertPublished(appId: string): Promise<[Response, MessageResponse]> {
|
||||
const [response, json] = await this.client.post(`/dev/${appId}/revert`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json).toEqual({
|
||||
message: "Reverted changes successfully.",
|
||||
})
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async revertUnpublished(appId: string): Promise<[Response, MessageResponse]> {
|
||||
const [response, json] = await this.client.post(`/dev/${appId}/revert`)
|
||||
expect(response).toHaveStatusCode(400)
|
||||
expect(json).toEqual({
|
||||
message: "App has not yet been deployed",
|
||||
status: 400,
|
||||
})
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async delete(appId: string): Promise<Response> {
|
||||
const [response, _] = await this.client.del(`/applications/${appId}`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return response
|
||||
}
|
||||
|
||||
async rename(
|
||||
appId: string,
|
||||
oldName: string,
|
||||
body: any
|
||||
): Promise<[Response, App]> {
|
||||
const [response, json] = await this.client.put(`/applications/${appId}`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.name).not.toEqual(oldName)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async addScreentoApp(body: any): Promise<[Response, App]> {
|
||||
const [response, json] = await this.client.post(`/screens`, { body })
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async getRoutes(screenExists?: boolean): Promise<[Response, RouteConfig]> {
|
||||
const [response, json] = await this.client.get(`/routing`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
if (screenExists) {
|
||||
expect(json.routes["/test"]).toBeTruthy()
|
||||
} else {
|
||||
expect(json.routes["/test"]).toBeUndefined()
|
||||
}
|
||||
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async unpublish(appId: string): Promise<[Response]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/applications/${appId}/unpublish`
|
||||
)
|
||||
expect(response).toHaveStatusCode(204)
|
||||
return [response]
|
||||
}
|
||||
|
||||
async unlock(appId: string): Promise<[Response, MessageResponse]> {
|
||||
const [response, json] = await this.client.del(`/dev/${appId}/lock`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.message).toEqual("Lock released successfully.")
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async updateIcon(appId: string): Promise<[Response, App]> {
|
||||
const body = {
|
||||
icon: {
|
||||
name: "ConversionFunnel",
|
||||
color: "var(--spectrum-global-color-red-400)",
|
||||
},
|
||||
}
|
||||
const [response, json] = await this.client.put(`/applications/${appId}`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.icon.name).toEqual(body.icon.name)
|
||||
expect(json.icon.color).toEqual(body.icon.color)
|
||||
return [response, json]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import { Response } from "node-fetch"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
import { APIRequestOpts, State } from "../../../types"
|
||||
|
||||
export default class AuthAPI {
|
||||
state: State
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(client: BudibaseInternalAPIClient, state: State) {
|
||||
this.client = client
|
||||
this.state = state
|
||||
}
|
||||
|
||||
async login(
|
||||
tenantId: string,
|
||||
email: String,
|
||||
password: String,
|
||||
opts: APIRequestOpts = { doExpect: true }
|
||||
): Promise<[Response, string]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/global/auth/${tenantId}/login`,
|
||||
{
|
||||
body: {
|
||||
username: email,
|
||||
password: password,
|
||||
},
|
||||
}
|
||||
)
|
||||
if (opts.doExpect) {
|
||||
expect(response).toHaveStatusCode(200)
|
||||
}
|
||||
const cookie = response.headers.get("set-cookie")
|
||||
return [response, cookie!]
|
||||
}
|
||||
|
||||
async logout(): Promise<any> {
|
||||
return this.client.post(`/global/auth/logout`)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { GetEnvironmentResponse } from "@budibase/types"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
import { APIRequestOpts } from "../../../types"
|
||||
|
||||
export default class EnvironmentAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async getEnvironment(
|
||||
opts: APIRequestOpts = { doExpect: true }
|
||||
): Promise<GetEnvironmentResponse> {
|
||||
const [response, json] = await this.client.get(`/system/environment`)
|
||||
if (opts.doExpect) {
|
||||
expect(response.status).toBe(200)
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Role, UserRoles } from "@budibase/types"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
|
||||
export default class RoleAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async getRoles(): Promise<[Response, Role[]]> {
|
||||
const [response, json] = await this.client.get(`/roles`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async createRole(body: Partial<UserRoles>): Promise<[Response, UserRoles]> {
|
||||
const [response, json] = await this.client.post(`/roles`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
}
|
|
@ -1,18 +1,18 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Row } from "@budibase/types"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
|
||||
export default class RowsApi {
|
||||
api: InternalAPIClient
|
||||
export default class RowAPI {
|
||||
rowAdded: boolean
|
||||
constructor(apiClient: InternalAPIClient) {
|
||||
this.api = apiClient
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
this.rowAdded = false
|
||||
}
|
||||
|
||||
async getAll(tableId: string): Promise<[Response, Row[]]> {
|
||||
const response = await this.api.get(`/${tableId}/rows`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/${tableId}/rows`)
|
||||
if (this.rowAdded) {
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBeGreaterThanOrEqual(1)
|
||||
|
@ -20,8 +20,9 @@ export default class RowsApi {
|
|||
return [response, json]
|
||||
}
|
||||
async add(tableId: string, body: any): Promise<[Response, Row]> {
|
||||
const response = await this.api.post(`/${tableId}/rows`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/${tableId}/rows`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toBeDefined()
|
||||
expect(json._rev).toBeDefined()
|
||||
|
@ -31,8 +32,9 @@ export default class RowsApi {
|
|||
}
|
||||
|
||||
async delete(tableId: string, body: any): Promise<[Response, Row[]]> {
|
||||
const response = await this.api.del(`/${tableId}/rows/`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.del(`/${tableId}/rows/`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
@ -41,8 +43,9 @@ export default class RowsApi {
|
|||
tableId: string,
|
||||
body: any
|
||||
): Promise<[Response, Row[]]> {
|
||||
const response = await this.api.post(`/${tableId}/search`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/${tableId}/search`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.hasNextPage).toEqual(false)
|
||||
return [response, json.rows]
|
||||
|
@ -52,8 +55,9 @@ export default class RowsApi {
|
|||
tableId: string,
|
||||
body: any
|
||||
): Promise<[Response, Row[]]> {
|
||||
const response = await this.api.post(`/${tableId}/search`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/${tableId}/search`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.hasNextPage).toEqual(true)
|
||||
expect(json.rows.length).toEqual(10)
|
|
@ -1,17 +1,16 @@
|
|||
import { Screen } from "@budibase/types"
|
||||
import { Response } from "node-fetch"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
|
||||
export default class ScreenApi {
|
||||
api: InternalAPIClient
|
||||
export default class ScreenAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(apiClient: InternalAPIClient) {
|
||||
this.api = apiClient
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async create(body: any): Promise<[Response, Screen]> {
|
||||
const response = await this.api.post(`/screens`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/screens`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toBeDefined()
|
||||
expect(json.routing.roleId).toBe(body.routing.roleId)
|
||||
|
@ -19,8 +18,9 @@ export default class ScreenApi {
|
|||
}
|
||||
|
||||
async delete(screenId: string, rev: string): Promise<[Response, Screen]> {
|
||||
const response = await this.api.del(`/screens/${screenId}/${rev}`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.del(
|
||||
`/screens/${screenId}/${rev}`
|
||||
)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { User } from "@budibase/types"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
import { ApiKeyResponse } from "../../../types"
|
||||
|
||||
export default class SelfAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async getSelf(): Promise<[Response, Partial<User>]> {
|
||||
const [response, json] = await this.client.get(`/global/self`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async changeSelfPassword(body: Partial<User>): Promise<[Response, User]> {
|
||||
const [response, json] = await this.client.post(`/global/self`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toEqual(body._id)
|
||||
expect(json._rev).not.toEqual(body._rev)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async getApiKey(): Promise<ApiKeyResponse> {
|
||||
const [response, json] = await this.client.get(`/global/self/api_key`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json).toHaveProperty("apiKey")
|
||||
return json
|
||||
}
|
||||
}
|
|
@ -1,34 +1,31 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Table } from "@budibase/types"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
import { responseMessage } from "../fixtures/types/responseMessage"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
import { MessageResponse } from "../../../types"
|
||||
|
||||
export default class TablesApi {
|
||||
api: InternalAPIClient
|
||||
export default class TableAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(apiClient: InternalAPIClient) {
|
||||
this.api = apiClient
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async getAll(expectedNumber: Number): Promise<[Response, Table[]]> {
|
||||
const response = await this.api.get(`/tables`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/tables`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBe(expectedNumber)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async getTableById(id: string): Promise<[Response, Table]> {
|
||||
const response = await this.api.get(`/tables/${id}`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/tables/${id}`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toEqual(id)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async save(body: any, columnAdded?: boolean): Promise<[Response, Table]> {
|
||||
const response = await this.api.post(`/tables`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/tables`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toBeDefined()
|
||||
expect(json._rev).toBeDefined()
|
||||
|
@ -40,8 +37,7 @@ export default class TablesApi {
|
|||
}
|
||||
|
||||
async forbiddenSave(body: any): Promise<[Response, Table]> {
|
||||
const response = await this.api.post(`/tables`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/tables`, { body })
|
||||
expect(response).toHaveStatusCode(403)
|
||||
|
||||
return [response, json]
|
||||
|
@ -50,9 +46,8 @@ export default class TablesApi {
|
|||
async delete(
|
||||
id: string,
|
||||
revId: string
|
||||
): Promise<[Response, responseMessage]> {
|
||||
const response = await this.api.del(`/tables/${id}/${revId}`)
|
||||
const json = await response.json()
|
||||
): Promise<[Response, MessageResponse]> {
|
||||
const [response, json] = await this.client.del(`/tables/${id}/${revId}`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.message).toEqual(`Table ${id} deleted.`)
|
||||
return [response, json]
|
|
@ -1,33 +1,30 @@
|
|||
import { Response } from "node-fetch"
|
||||
import { Role, User, UserDeletedEvent, UserRoles } from "@budibase/types"
|
||||
import InternalAPIClient from "./InternalAPIClient"
|
||||
import { responseMessage } from "../fixtures/types/responseMessage"
|
||||
import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient"
|
||||
import { MessageResponse } from "../../../types"
|
||||
|
||||
export default class UserManagementApi {
|
||||
api: InternalAPIClient
|
||||
export default class UserAPI {
|
||||
client: BudibaseInternalAPIClient
|
||||
|
||||
constructor(apiClient: InternalAPIClient) {
|
||||
this.api = apiClient
|
||||
constructor(client: BudibaseInternalAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async search(): Promise<[Response, Partial<User>[]]> {
|
||||
const response = await this.api.post(`/global/users/search`, {})
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/global/users/search`, {})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.data.length).toBeGreaterThan(0)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async getSelf(): Promise<[Response, Partial<User>]> {
|
||||
const response = await this.api.get(`/global/self`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/global/self`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async getAll(): Promise<[Response, Partial<User>[]]> {
|
||||
const response = await this.api.get(`/global/users`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/global/users`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.length).toBeGreaterThan(0)
|
||||
return [response, json]
|
||||
|
@ -41,22 +38,24 @@ export default class UserManagementApi {
|
|||
groups: [],
|
||||
},
|
||||
}
|
||||
const response = await this.api.post(`/global/users/bulk`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/global/users/bulk`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.created.unsuccessful.length).toEqual(0)
|
||||
expect(json.created.successful.length).toEqual(body.create.users.length)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async deleteMultiple(userId: string[]): Promise<[Response, responseMessage]> {
|
||||
async deleteMultiple(userId: string[]): Promise<[Response, MessageResponse]> {
|
||||
const body = {
|
||||
delete: {
|
||||
userIds: [userId],
|
||||
},
|
||||
}
|
||||
const response = await this.api.post(`/global/users/bulk`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/global/users/bulk`, {
|
||||
body,
|
||||
})
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.deleted.successful.length).toEqual(1)
|
||||
expect(json.deleted.unsuccessful.length).toEqual(0)
|
||||
|
@ -64,16 +63,17 @@ export default class UserManagementApi {
|
|||
return [response, json]
|
||||
}
|
||||
async delete(userId: string): Promise<[Response, UserDeletedEvent]> {
|
||||
const response = await this.api.del(`/global/users/${userId}`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.del(`/global/users/${userId}`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.message).toEqual(`User ${userId} deleted.`)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async invite(body: any): Promise<[Response, responseMessage]> {
|
||||
const response = await this.api.post(`/global/users/multi/invite`, { body })
|
||||
const json = await response.json()
|
||||
async invite(body: any): Promise<[Response, MessageResponse]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/global/users/multi/invite`,
|
||||
{ body }
|
||||
)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json.unsuccessful.length).toEqual(0)
|
||||
expect(json.successful.length).toEqual(body.length)
|
||||
|
@ -82,15 +82,13 @@ export default class UserManagementApi {
|
|||
}
|
||||
|
||||
async getRoles(): Promise<[Response, Role[]]> {
|
||||
const response = await this.api.get(`/roles`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/roles`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async updateInfo(body: any): Promise<[Response, User]> {
|
||||
const response = await this.api.post(`/global/users/`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/global/users/`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toEqual(body._id)
|
||||
expect(json._rev).not.toEqual(body._rev)
|
||||
|
@ -98,8 +96,7 @@ export default class UserManagementApi {
|
|||
}
|
||||
|
||||
async forcePasswordReset(body: any): Promise<[Response, User]> {
|
||||
const response = await this.api.post(`/global/users/`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/global/users/`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toEqual(body._id)
|
||||
expect(json._rev).not.toEqual(body._rev)
|
||||
|
@ -107,15 +104,13 @@ export default class UserManagementApi {
|
|||
}
|
||||
|
||||
async getInfo(userId: string): Promise<[Response, User]> {
|
||||
const response = await this.api.get(`/global/users/${userId}`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/global/users/${userId}`)
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
||||
|
||||
async changeSelfPassword(body: Partial<User>): Promise<[Response, User]> {
|
||||
const response = await this.api.post(`/global/self`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/global/self`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
expect(json._id).toEqual(body._id)
|
||||
expect(json._rev).not.toEqual(body._rev)
|
||||
|
@ -123,8 +118,7 @@ export default class UserManagementApi {
|
|||
}
|
||||
|
||||
async createRole(body: Partial<UserRoles>): Promise<[Response, UserRoles]> {
|
||||
const response = await this.api.post(`/roles`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/roles`, { body })
|
||||
expect(response).toHaveStatusCode(200)
|
||||
return [response, json]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { default as BudibaseInternalAPI } from "./BudibaseInternalAPI"
|
|
@ -0,0 +1,25 @@
|
|||
import { BudibaseInternalAPI } from "../api"
|
||||
import { BudibaseTestConfiguration } from "../../shared"
|
||||
|
||||
export default class TestConfiguration<T> extends BudibaseTestConfiguration {
|
||||
// apis
|
||||
api: BudibaseInternalAPI
|
||||
|
||||
// context
|
||||
context: T
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
// for brevity
|
||||
this.api = this.internalApi
|
||||
this.context = <T>{}
|
||||
}
|
||||
|
||||
async beforeAll() {
|
||||
await super.beforeAll()
|
||||
}
|
||||
|
||||
async afterAll() {
|
||||
await super.afterAll()
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { default as TestConfiguration } from "./TestConfiguration"
|
|
@ -0,0 +1,20 @@
|
|||
import { generator } from "../../shared"
|
||||
import { Hosting, CreateAccountRequest } from "@budibase/types"
|
||||
|
||||
export const generateAccount = (): CreateAccountRequest => {
|
||||
const uuid = generator.guid()
|
||||
|
||||
const email = `qa+${uuid}@budibase.com`
|
||||
const tenant = `tenant${uuid.replace(/-/g, "")}`
|
||||
|
||||
return {
|
||||
email,
|
||||
hosting: Hosting.CLOUD,
|
||||
name: email,
|
||||
password: uuid,
|
||||
profession: "software_engineer",
|
||||
size: "10+",
|
||||
tenantId: tenant,
|
||||
tenantName: tenant,
|
||||
}
|
||||
}
|
|
@ -1,21 +1,20 @@
|
|||
import generator from "../../generator"
|
||||
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { Template } from "@budibase/types"
|
||||
import { App } from "@budibase/types"
|
||||
import { generator } from "../../shared"
|
||||
import { CreateAppRequest } from "../../types"
|
||||
|
||||
export const generateApp = (
|
||||
overrides: Partial<Application> = {}
|
||||
): Partial<Application> => ({
|
||||
overrides: Partial<CreateAppRequest> = {}
|
||||
): CreateAppRequest => ({
|
||||
name: generator.word() + generator.hash(),
|
||||
url: `/${generator.word() + generator.hash()}`,
|
||||
...overrides,
|
||||
})
|
||||
|
||||
// Applications type doesn't work here, save to add useTemplate parameter?
|
||||
export const appFromTemplate = (): any => {
|
||||
export const appFromTemplate = (): CreateAppRequest => {
|
||||
return {
|
||||
name: generator.word(),
|
||||
url: `/${generator.word()}`,
|
||||
// @ts-ignore
|
||||
useTemplate: "true",
|
||||
templateName: "Near Miss Register",
|
||||
templateKey: "app/near-miss-register",
|
|
@ -0,0 +1,6 @@
|
|||
export * as accounts from "./accounts"
|
||||
export * as apps from "./applications"
|
||||
export * as rows from "./rows"
|
||||
export * as screens from "./screens"
|
||||
export * as tables from "./tables"
|
||||
export * as users from "./users"
|
|
@ -1,8 +1,8 @@
|
|||
import generator from "../../generator"
|
||||
import { generator } from "../../shared"
|
||||
|
||||
const randomId = generator.guid()
|
||||
|
||||
const generateScreen = (roleId: string): any => ({
|
||||
export const generateScreen = (roleId: string): any => ({
|
||||
showNavigation: true,
|
||||
width: "Large",
|
||||
name: randomId,
|
||||
|
@ -30,5 +30,3 @@ const generateScreen = (roleId: string): any => ({
|
|||
homeScreen: false,
|
||||
},
|
||||
})
|
||||
|
||||
export default generateScreen
|
|
@ -1,10 +1,10 @@
|
|||
import generator from "../../generator"
|
||||
import { generator } from "../../shared"
|
||||
import { User } from "@budibase/types"
|
||||
|
||||
const generateDeveloper = (): Partial<User> => {
|
||||
const randomId = generator.guid()
|
||||
return {
|
||||
email: `pedro+${randomId}@budibase.com`,
|
||||
email: `${randomId}@budibase.com`,
|
||||
password: randomId,
|
||||
roles: {},
|
||||
forceResetPassword: true,
|
||||
|
@ -17,7 +17,7 @@ const generateDeveloper = (): Partial<User> => {
|
|||
const generateAdmin = (): Partial<User> => {
|
||||
const randomId = generator.guid()
|
||||
return {
|
||||
email: `pedro+${randomId}@budibase.com`,
|
||||
email: `${randomId}@budibase.com`,
|
||||
password: randomId,
|
||||
roles: {},
|
||||
forceResetPassword: true,
|
||||
|
@ -32,7 +32,7 @@ const generateAdmin = (): Partial<User> => {
|
|||
const generateAppUser = (): Partial<User> => {
|
||||
const randomId = generator.guid()
|
||||
return {
|
||||
email: `pedro+${randomId}@budibase.com`,
|
||||
email: `${randomId}@budibase.com`,
|
||||
password: randomId,
|
||||
roles: {},
|
||||
forceResetPassword: true,
|
||||
|
@ -49,7 +49,7 @@ export const generateInviteUser = (): Object[] => {
|
|||
const randomId = generator.guid()
|
||||
return [
|
||||
{
|
||||
email: `pedro+${randomId}@budibase.com`,
|
||||
email: `${randomId}@budibase.com`,
|
||||
userInfo: {
|
||||
userGroups: [],
|
||||
},
|
|
@ -0,0 +1 @@
|
|||
export * from "./api"
|
|
@ -0,0 +1,42 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - Application creation", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Get applications without applications", async () => {
|
||||
await config.api.apps.fetchEmptyAppList()
|
||||
})
|
||||
|
||||
it("Get all Applications after creating an application", async () => {
|
||||
await config.api.apps.create({
|
||||
...fixtures.apps.generateApp(),
|
||||
useTemplate: "false",
|
||||
})
|
||||
|
||||
await config.api.apps.fetchAllApplications()
|
||||
})
|
||||
|
||||
it("Get application details", async () => {
|
||||
const app = await config.createApp({
|
||||
...fixtures.apps.generateApp(),
|
||||
useTemplate: "false",
|
||||
})
|
||||
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.application.name).toEqual(app.name)
|
||||
expect(appPackageJson.application.version).toEqual(app.version)
|
||||
expect(appPackageJson.application.url).toEqual(app.url)
|
||||
expect(appPackageJson.application.tenantId).toEqual(app.tenantId)
|
||||
expect(appPackageJson.application.status).toEqual(app.status)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,19 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
|
||||
describe("Internal API - Application creation, update, publish and delete", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("DELETE - Delete an application", async () => {
|
||||
const app = await config.createApp()
|
||||
|
||||
await config.api.apps.delete(app.appId!)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,54 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { db } from "@budibase/backend-core"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - Application creation, update, publish and delete", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Publish app", async () => {
|
||||
// create the app
|
||||
const app = await config.createApp(fixtures.apps.appFromTemplate())
|
||||
|
||||
// check preview renders
|
||||
await config.api.apps.canRender()
|
||||
|
||||
// publish app
|
||||
await config.api.apps.publish(app.appId)
|
||||
|
||||
// check published app renders
|
||||
config.state.appId = db.getProdAppID(app.appId!)
|
||||
await config.api.apps.canRender()
|
||||
|
||||
// unpublish app
|
||||
await config.api.apps.unpublish(app.appId!)
|
||||
})
|
||||
|
||||
it("Sync application before deployment", async () => {
|
||||
const app = await config.createApp()
|
||||
|
||||
const [syncResponse, sync] = await config.api.apps.sync(app.appId!)
|
||||
expect(sync).toEqual({
|
||||
message: "App sync not required, app not deployed.",
|
||||
})
|
||||
})
|
||||
|
||||
it("Sync application after deployment", async () => {
|
||||
const app = await config.createApp()
|
||||
|
||||
// publish app
|
||||
await config.api.apps.publish(app._id)
|
||||
|
||||
const [syncResponse, sync] = await config.api.apps.sync(app.appId!)
|
||||
expect(sync).toEqual({
|
||||
message: "App sync completed successfully.",
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,45 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { generator } from "../../../shared"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - Application creation, update, publish and delete", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Update an application", async () => {
|
||||
const app = await config.createApp()
|
||||
|
||||
await config.api.apps.rename(app.appId!, app.name!, {
|
||||
name: generator.word(),
|
||||
})
|
||||
})
|
||||
|
||||
it("Revert Changes without changes", async () => {
|
||||
const app = await config.createApp()
|
||||
|
||||
await config.api.apps.revertUnpublished(app.appId!)
|
||||
})
|
||||
|
||||
it("Revert Changes", async () => {
|
||||
const app = await config.createApp()
|
||||
|
||||
// publish app
|
||||
await config.api.apps.publish(app._id)
|
||||
|
||||
// Change/add component to the app
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
|
||||
// // Revert the app to published state
|
||||
await config.api.apps.revertPublished(app.appId!)
|
||||
|
||||
// Check screen is removed
|
||||
await config.api.apps.getRoutes()
|
||||
})
|
||||
})
|
|
@ -0,0 +1,27 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - Data Sources", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Create an app with a data source", async () => {
|
||||
// Create app
|
||||
await config.createApp()
|
||||
|
||||
// Create Screen
|
||||
const roleArray = ["BASIC", "POWER", "ADMIN", "PUBLIC"]
|
||||
for (let role in roleArray) {
|
||||
const [response, screen] = await config.api.screens.create(
|
||||
fixtures.screens.generateScreen(roleArray[role])
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
|
@ -0,0 +1,51 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - /screens endpoints", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Create a screen with each role type", async () => {
|
||||
// Create app
|
||||
await config.createApp()
|
||||
|
||||
// Create Screen
|
||||
const roleArray = ["BASIC", "POWER", "ADMIN", "PUBLIC"]
|
||||
for (let role in roleArray) {
|
||||
const [response, screen] = await config.api.screens.create(
|
||||
fixtures.screens.generateScreen(roleArray[role])
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("Get screens", async () => {
|
||||
// Create app
|
||||
await config.createApp()
|
||||
|
||||
// Create Screen
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
|
||||
// Check screen exists
|
||||
await config.api.apps.getRoutes(true)
|
||||
})
|
||||
|
||||
it("Delete a screen", async () => {
|
||||
// Create app
|
||||
await config.createApp()
|
||||
|
||||
// Create Screen
|
||||
const [screenResponse, screen] = await config.api.screens.create(
|
||||
fixtures.screens.generateScreen("BASIC")
|
||||
)
|
||||
|
||||
// Delete Screen
|
||||
await config.api.screens.delete(screen._id!, screen._rev!)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,144 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { generator } from "../../../shared"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - Table Operations", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
async function createAppFromTemplate() {
|
||||
return config.api.apps.create({
|
||||
name: generator.word(),
|
||||
url: `/${generator.word()}`,
|
||||
useTemplate: "true",
|
||||
templateName: "Near Miss Register",
|
||||
templateKey: "app/near-miss-register",
|
||||
templateFile: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
it("Create and delete table, columns and rows", async () => {
|
||||
// create the app
|
||||
await config.createApp(fixtures.apps.appFromTemplate())
|
||||
|
||||
// Get current tables: expect 2 in this template
|
||||
await config.api.tables.getAll(2)
|
||||
|
||||
// Add new table
|
||||
const [createdTableResponse, createdTableData] =
|
||||
await config.api.tables.save(fixtures.tables.generateTable())
|
||||
|
||||
//Table was added
|
||||
await config.api.tables.getAll(3)
|
||||
|
||||
//Get information about the table
|
||||
await config.api.tables.getTableById(createdTableData._id!)
|
||||
|
||||
//Add Column to table
|
||||
const newColumn =
|
||||
fixtures.tables.generateNewColumnForTable(createdTableData)
|
||||
const [addColumnResponse, addColumnData] = await config.api.tables.save(
|
||||
newColumn,
|
||||
true
|
||||
)
|
||||
|
||||
//Add Row to table
|
||||
const newRow = fixtures.rows.generateNewRowForTable(addColumnData._id!)
|
||||
await config.api.rows.add(addColumnData._id!, newRow)
|
||||
|
||||
//Get Row from table
|
||||
const [getRowResponse, getRowData] = await config.api.rows.getAll(
|
||||
addColumnData._id!
|
||||
)
|
||||
|
||||
//Delete Row from table
|
||||
const rowToDelete = {
|
||||
rows: [getRowData[0]],
|
||||
}
|
||||
const [deleteRowResponse, deleteRowData] = await config.api.rows.delete(
|
||||
addColumnData._id!,
|
||||
rowToDelete
|
||||
)
|
||||
expect(deleteRowData[0]._id).toEqual(getRowData[0]._id)
|
||||
|
||||
//Delete the table
|
||||
const [deleteTableResponse, deleteTable] = await config.api.tables.delete(
|
||||
addColumnData._id!,
|
||||
addColumnData._rev!
|
||||
)
|
||||
|
||||
//Table was deleted
|
||||
await config.api.tables.getAll(2)
|
||||
})
|
||||
|
||||
it("Search and pagination", async () => {
|
||||
// create the app
|
||||
await config.createApp(fixtures.apps.appFromTemplate())
|
||||
|
||||
// Get current tables: expect 2 in this template
|
||||
await config.api.tables.getAll(2)
|
||||
|
||||
// Add new table
|
||||
const [createdTableResponse, createdTableData] =
|
||||
await config.api.tables.save(fixtures.tables.generateTable())
|
||||
|
||||
//Table was added
|
||||
await config.api.tables.getAll(3)
|
||||
|
||||
//Get information about the table
|
||||
await config.api.tables.getTableById(createdTableData._id!)
|
||||
|
||||
//Add Column to table
|
||||
const newColumn =
|
||||
fixtures.tables.generateNewColumnForTable(createdTableData)
|
||||
const [addColumnResponse, addColumnData] = await config.api.tables.save(
|
||||
newColumn,
|
||||
true
|
||||
)
|
||||
|
||||
//Add Row to table
|
||||
let newRow = fixtures.rows.generateNewRowForTable(addColumnData._id!)
|
||||
await config.api.rows.add(addColumnData._id!, newRow)
|
||||
|
||||
//Search single row
|
||||
await config.api.rows.searchNoPagination(
|
||||
createdTableData._id!,
|
||||
fixtures.rows.searchBody(createdTableData.primaryDisplay!)
|
||||
)
|
||||
|
||||
//Add 10 more rows
|
||||
for (let i = 0; i < 10; i++) {
|
||||
let newRow = fixtures.rows.generateNewRowForTable(addColumnData._id!)
|
||||
await config.api.rows.add(addColumnData._id!, newRow)
|
||||
}
|
||||
|
||||
//Search rows with pagination
|
||||
const [allRowsResponse, allRowsJson] =
|
||||
await config.api.rows.searchWithPagination(
|
||||
createdTableData._id!,
|
||||
fixtures.rows.searchBody(createdTableData.primaryDisplay!)
|
||||
)
|
||||
|
||||
//Delete Rows from table
|
||||
const rowToDelete = {
|
||||
rows: [allRowsJson],
|
||||
}
|
||||
const [deleteRowResponse, deleteRowData] = await config.api.rows.delete(
|
||||
createdTableData._id!,
|
||||
rowToDelete
|
||||
)
|
||||
|
||||
//Search single row
|
||||
await config.api.rows.searchWithPagination(
|
||||
createdTableData._id!,
|
||||
fixtures.rows.searchBody(createdTableData.primaryDisplay!)
|
||||
)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,107 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { User } from "@budibase/types"
|
||||
import { db } from "@budibase/backend-core"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
describe("Internal API - App Specific Roles & Permissions", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
// Before each test, login as admin. Some tests will require login as a different user
|
||||
beforeAll(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Add BASIC user to app", async () => {
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
const app = await config.createApp(fixtures.apps.appFromTemplate())
|
||||
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
roles: {
|
||||
[app.appId!]: "BASIC",
|
||||
},
|
||||
}
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toEqual("BASIC")
|
||||
})
|
||||
|
||||
it("Add ADMIN user to app", async () => {
|
||||
// Create a user with ADMIN role and check if it was created successfully
|
||||
const adminUser = fixtures.users.generateUser(1, "admin")
|
||||
expect(adminUser[0].builder?.global).toEqual(true)
|
||||
expect(adminUser[0].admin?.global).toEqual(true)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(adminUser)
|
||||
|
||||
// const app = await config.createApp(fixtures.apps.appFromTemplate())
|
||||
const app = await config.createApp(fixtures.apps.appFromTemplate())
|
||||
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
roles: {
|
||||
[app.appId!]: "ADMIN",
|
||||
},
|
||||
}
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toEqual("ADMIN")
|
||||
|
||||
// publish app
|
||||
await config.api.apps.publish(app.appId)
|
||||
// check published app renders
|
||||
config.state.appId = db.getProdAppID(app.appId!)
|
||||
await config.api.apps.canRender()
|
||||
})
|
||||
|
||||
it("Add POWER user to app", async () => {
|
||||
const powerUser = fixtures.users.generateUser(1, "developer")
|
||||
expect(powerUser[0].builder?.global).toEqual(true)
|
||||
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(powerUser)
|
||||
|
||||
const app = await config.createApp()
|
||||
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
roles: {
|
||||
[app.appId!]: "POWER",
|
||||
},
|
||||
}
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
// Get the user information again and check if the role was added
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toEqual("POWER")
|
||||
})
|
||||
})
|
|
@ -1,27 +1,16 @@
|
|||
import TestConfiguration from "../../../config/internal-api/TestConfiguration"
|
||||
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
|
||||
import AccountsAPIClient from "../../../config/internal-api/TestConfiguration/accountsAPIClient"
|
||||
import { generateApp } from "../../../config/internal-api/fixtures/applications"
|
||||
import { generateUser } from "../../../config/internal-api/fixtures/userManagement"
|
||||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { App, User } from "@budibase/types"
|
||||
import generateScreen from "../../../config/internal-api/fixtures/screens"
|
||||
import { db } from "@budibase/backend-core"
|
||||
import * as fixtures from "./../../fixtures"
|
||||
|
||||
describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
||||
let api: InternalAPIClient
|
||||
let accountsAPI: AccountsAPIClient
|
||||
let config: TestConfiguration<Application>
|
||||
const config = new TestConfiguration()
|
||||
let app: Partial<App>
|
||||
|
||||
// Before each test, login as admin. Some tests will require login as a different user
|
||||
beforeEach(async () => {
|
||||
api = new InternalAPIClient()
|
||||
accountsAPI = new AccountsAPIClient()
|
||||
config = new TestConfiguration<Application>(api, accountsAPI)
|
||||
await config.setupAccountAndTenant()
|
||||
app = await config.applications.create(generateApp())
|
||||
config.applications.api.appId = app.appId
|
||||
await config.beforeAll()
|
||||
app = await config.createApp()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
|
@ -30,12 +19,11 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
|
||||
it("Custom role access for level 1 permissions", async () => {
|
||||
// Set up user
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
//Create level 1 role
|
||||
const role = {
|
||||
|
@ -43,12 +31,11 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
permissionId: "public",
|
||||
name: "level 1",
|
||||
}
|
||||
const [createRoleResponse, createRoleJson] = await config.users.createRole(
|
||||
role
|
||||
)
|
||||
const [createRoleResponse, createRoleJson] =
|
||||
await config.api.users.createRole(role)
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -60,42 +47,45 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
[prodAppId]: createRoleJson._id,
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id)
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with level 1 user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
await config.login(
|
||||
config.state.tenantId!,
|
||||
appUser[0].email!,
|
||||
appUser[0].password!
|
||||
)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(1)
|
||||
})
|
||||
it("Custom role access for level 2 permissions", async () => {
|
||||
// Set up user
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
|
||||
|
@ -105,12 +95,11 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
permissionId: "read_only",
|
||||
name: "level 2",
|
||||
}
|
||||
const [createRoleResponse, createRoleJson] = await config.users.createRole(
|
||||
role
|
||||
)
|
||||
const [createRoleResponse, createRoleJson] =
|
||||
await config.api.users.createRole(role)
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -122,40 +111,39 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
[prodAppId]: createRoleJson._id,
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id)
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with level 1 user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(1)
|
||||
})
|
||||
it("Custom role access for level 3 permissions", async () => {
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
|
||||
|
@ -165,12 +153,11 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
permissionId: "write",
|
||||
name: "level 3",
|
||||
}
|
||||
const [createRoleResponse, createRoleJson] = await config.users.createRole(
|
||||
role
|
||||
)
|
||||
const [createRoleResponse, createRoleJson] =
|
||||
await config.api.users.createRole(role)
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -182,40 +169,39 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
[prodAppId]: createRoleJson._id,
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id)
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with level 1 user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(1)
|
||||
})
|
||||
it("Custom role access for level 4 permissions", async () => {
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
|
||||
|
@ -225,12 +211,11 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
permissionId: "power",
|
||||
name: "level 4",
|
||||
}
|
||||
const [createRoleResponse, createRoleJson] = await config.users.createRole(
|
||||
role
|
||||
)
|
||||
const [createRoleResponse, createRoleJson] =
|
||||
await config.api.users.createRole(role)
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -242,40 +227,39 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
[prodAppId]: createRoleJson._id,
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id)
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with level 1 user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(1)
|
||||
})
|
||||
it("Custom role access for level 5 permissions", async () => {
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
|
||||
|
@ -285,12 +269,11 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
permissionId: "admin",
|
||||
name: "level 5",
|
||||
}
|
||||
const [createRoleResponse, createRoleJson] = await config.users.createRole(
|
||||
role
|
||||
)
|
||||
const [createRoleResponse, createRoleJson] =
|
||||
await config.api.users.createRole(role)
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -302,30 +285,30 @@ describe.skip("Internal API - App Specific Roles & Permissions", () => {
|
|||
[prodAppId]: createRoleJson._id,
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual(createRoleJson._id)
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with level 1 user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(1)
|
||||
})
|
|
@ -1,24 +1,14 @@
|
|||
import TestConfiguration from "../../../config/internal-api/TestConfiguration"
|
||||
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
|
||||
import AccountsAPIClient from "../../../config/internal-api/TestConfiguration/accountsAPIClient"
|
||||
import { generateApp } from "../../../config/internal-api/fixtures/applications"
|
||||
import { generateUser } from "../../../config/internal-api/fixtures/userManagement"
|
||||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { User } from "@budibase/types"
|
||||
import generateScreen from "../../../config/internal-api/fixtures/screens"
|
||||
import { db } from "@budibase/backend-core"
|
||||
import * as fixtures from "./../../fixtures"
|
||||
|
||||
describe.skip("Internal API - Role screen access", () => {
|
||||
let api: InternalAPIClient
|
||||
let accountsAPI: AccountsAPIClient
|
||||
let config: TestConfiguration<Application>
|
||||
const config = new TestConfiguration()
|
||||
|
||||
// Before each test, login as admin. Some tests will require login as a different user
|
||||
beforeEach(async () => {
|
||||
api = new InternalAPIClient()
|
||||
accountsAPI = new AccountsAPIClient()
|
||||
config = new TestConfiguration<Application>(api, accountsAPI)
|
||||
await config.setupAccountAndTenant()
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
|
@ -27,19 +17,17 @@ describe.skip("Internal API - Role screen access", () => {
|
|||
|
||||
it("Check Screen access for BASIC Role", async () => {
|
||||
// Set up user
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
const app = await config.applications.create(generateApp())
|
||||
config.applications.api.appId = app.appId
|
||||
const app = await config.createApp()
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -51,31 +39,31 @@ describe.skip("Internal API - Role screen access", () => {
|
|||
[prodAppId]: "BASIC",
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual("BASIC")
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with BASIC user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(1)
|
||||
expect(appPackageJson.screens[0].routing.roleId).toEqual("BASIC")
|
||||
|
@ -83,19 +71,17 @@ describe.skip("Internal API - Role screen access", () => {
|
|||
|
||||
it("Check Screen access for POWER role", async () => {
|
||||
// Set up user
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
const app = await config.applications.create(generateApp())
|
||||
config.applications.api.appId = app.appId
|
||||
const app = await config.createApp()
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -107,49 +93,47 @@ describe.skip("Internal API - Role screen access", () => {
|
|||
[prodAppId]: "POWER",
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual("POWER")
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with POWER user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(2)
|
||||
})
|
||||
|
||||
it("Check Screen access for ADMIN role", async () => {
|
||||
// Set up user
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] = await config.users.addMultiple(
|
||||
appUser
|
||||
)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
// Create App
|
||||
const app = await config.applications.create(generateApp())
|
||||
config.applications.api.appId = app.appId
|
||||
const app = await config.createApp()
|
||||
|
||||
// Update user roles
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const prodAppId = db.getProdAppID(app.appId!)
|
||||
|
@ -161,30 +145,30 @@ describe.skip("Internal API - Role screen access", () => {
|
|||
[prodAppId]: "ADMIN",
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[prodAppId]).toEqual("ADMIN")
|
||||
|
||||
await config.screen.create(generateScreen("BASIC"))
|
||||
await config.screen.create(generateScreen("POWER"))
|
||||
await config.screen.create(generateScreen("ADMIN"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("BASIC"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("POWER"))
|
||||
await config.api.screens.create(fixtures.screens.generateScreen("ADMIN"))
|
||||
|
||||
await config.applications.publish(<string>app.appId)
|
||||
await config.api.apps.publish(app.appId)
|
||||
const [firstappPackageResponse, firstappPackageJson] =
|
||||
await config.applications.getAppPackage(<string>app.appId)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(firstappPackageJson.screens).toBeDefined()
|
||||
expect(firstappPackageJson.screens.length).toEqual(3)
|
||||
|
||||
// login with ADMIN user
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
const [selfInfoResponse, selfInfoJson] = await config.users.getSelf()
|
||||
const [selfInfoResponse, selfInfoJson] = await config.api.users.getSelf()
|
||||
|
||||
// fetch app package
|
||||
const [appPackageResponse, appPackageJson] =
|
||||
await config.applications.getAppPackage(app.appId!)
|
||||
await config.api.apps.getAppPackage(app.appId!)
|
||||
expect(appPackageJson.screens).toBeDefined()
|
||||
expect(appPackageJson.screens.length).toEqual(3)
|
||||
})
|
|
@ -0,0 +1,123 @@
|
|||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { User } from "@budibase/types"
|
||||
import * as fixtures from "./../../fixtures"
|
||||
|
||||
describe.skip("Internal API - Role table access", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
// Before each test, login as admin. Some tests will require login as a different user
|
||||
beforeEach(async () => {
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await config.afterAll()
|
||||
})
|
||||
|
||||
it("Check Table access for app user", async () => {
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(appUser)
|
||||
|
||||
const app = await config.createApp()
|
||||
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
roles: {
|
||||
[app.appId!]: "BASIC",
|
||||
},
|
||||
}
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toEqual("BASIC")
|
||||
|
||||
const [createdTableResponse, createdTableData] =
|
||||
await config.api.tables.save(fixtures.tables.generateTable())
|
||||
|
||||
await config.login(appUser[0].email!, appUser[0].password!)
|
||||
|
||||
const newColumn =
|
||||
fixtures.tables.generateNewColumnForTable(createdTableData)
|
||||
await config.api.tables.forbiddenSave(newColumn)
|
||||
await config.api.tables.forbiddenSave(fixtures.tables.generateTable())
|
||||
})
|
||||
|
||||
it.skip("Check Table access for developer", async () => {
|
||||
const developer = fixtures.users.generateUser(1, "developer")
|
||||
expect(developer[0].builder?.global).toEqual(true)
|
||||
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(developer)
|
||||
|
||||
const app = await config.createApp()
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
roles: {
|
||||
[app.appId!]: "POWER",
|
||||
},
|
||||
}
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toEqual("POWER")
|
||||
|
||||
const [createdTableResponse, createdTableData] =
|
||||
await config.api.tables.save(fixtures.tables.generateTable())
|
||||
await config.login(developer[0].email!, developer[0].password!)
|
||||
const newColumn =
|
||||
fixtures.tables.generateNewColumnForTable(createdTableData)
|
||||
const [addColumnResponse, addColumnData] = await config.api.tables.save(
|
||||
newColumn,
|
||||
true
|
||||
)
|
||||
})
|
||||
|
||||
it.skip("Check Table access for admin", async () => {
|
||||
const adminUser = fixtures.users.generateUser(1, "admin")
|
||||
expect(adminUser[0].builder?.global).toEqual(true)
|
||||
expect(adminUser[0].admin?.global).toEqual(true)
|
||||
const [createUserResponse, createUserJson] =
|
||||
await config.api.users.addMultiple(adminUser)
|
||||
|
||||
const app = await config.createApp()
|
||||
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
createUserJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
roles: {
|
||||
[app.appId!]: "ADMIN",
|
||||
},
|
||||
}
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.api.users.getInfo(createUserJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toBeDefined()
|
||||
expect(changedUserInfoJson.roles[app.appId!]).toEqual("ADMIN")
|
||||
|
||||
await config.login(adminUser[0].email!, adminUser[0].password!)
|
||||
const [createdTableResponse, createdTableData] =
|
||||
await config.api.tables.save(fixtures.tables.generateTable())
|
||||
const newColumn =
|
||||
fixtures.tables.generateNewColumnForTable(createdTableData)
|
||||
const [addColumnResponse, addColumnData] = await config.api.tables.save(
|
||||
newColumn,
|
||||
true
|
||||
)
|
||||
})
|
||||
})
|
|
@ -1,18 +1,13 @@
|
|||
import TestConfiguration from "../../../config/internal-api/TestConfiguration"
|
||||
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
|
||||
import AccountsAPIClient from "../../../config/internal-api/TestConfiguration/accountsAPIClient"
|
||||
import { generateUser } from "../../../config/internal-api/fixtures/userManagement"
|
||||
import TestConfiguration from "../../config/TestConfiguration"
|
||||
import { User } from "@budibase/types"
|
||||
import * as fixtures from "./../../fixtures"
|
||||
|
||||
describe("Internal API - User Management & Permissions", () => {
|
||||
const api = new InternalAPIClient()
|
||||
const accountsAPI = new AccountsAPIClient()
|
||||
const config = new TestConfiguration<Application>(api, accountsAPI)
|
||||
const config = new TestConfiguration()
|
||||
|
||||
// Before each test, login as admin. Some tests will require login as a different user
|
||||
beforeAll(async () => {
|
||||
await config.setupAccountAndTenant()
|
||||
await config.beforeAll()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
|
@ -21,60 +16,60 @@ describe("Internal API - User Management & Permissions", () => {
|
|||
|
||||
it("Add Users with different roles", async () => {
|
||||
// Get all users
|
||||
await config.users.search()
|
||||
await config.api.users.search()
|
||||
|
||||
// Get all roles
|
||||
await config.users.getRoles()
|
||||
await config.api.users.getRoles()
|
||||
|
||||
// Add users with each role
|
||||
const admin = generateUser(1, "admin")
|
||||
const admin = fixtures.users.generateUser(1, "admin")
|
||||
expect(admin[0].builder?.global).toEqual(true)
|
||||
expect(admin[0].admin?.global).toEqual(true)
|
||||
const developer = generateUser(1, "developer")
|
||||
const developer = fixtures.users.generateUser(1, "developer")
|
||||
expect(developer[0].builder?.global).toEqual(true)
|
||||
const appUser = generateUser(1, "appUser")
|
||||
const appUser = fixtures.users.generateUser(1, "appUser")
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
|
||||
const userList = [...admin, ...developer, ...appUser]
|
||||
|
||||
await config.users.addMultiple(userList)
|
||||
await config.api.users.addMultiple(userList)
|
||||
|
||||
// Check users are added
|
||||
const [allUsersResponse, allUsersJson] = await config.users.getAll()
|
||||
const [allUsersResponse, allUsersJson] = await config.api.users.getAll()
|
||||
expect(allUsersJson.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it("Delete User", async () => {
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [userResponse, userJson] = await config.users.addMultiple(appUser)
|
||||
const [userResponse, userJson] = await config.api.users.addMultiple(appUser)
|
||||
const userId = userJson.created.successful[0]._id
|
||||
await config.users.delete(<string>userId)
|
||||
await config.api.users.delete(userId)
|
||||
})
|
||||
|
||||
it("Reset Password", async () => {
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [userResponse, userJson] = await config.users.addMultiple(appUser)
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userResponse, userJson] = await config.api.users.addMultiple(appUser)
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
userJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
...userInfoJson,
|
||||
password: "newPassword",
|
||||
}
|
||||
await config.users.forcePasswordReset(body)
|
||||
await config.api.users.forcePasswordReset(body)
|
||||
})
|
||||
|
||||
it("Change User information", async () => {
|
||||
const appUser = generateUser()
|
||||
const appUser = fixtures.users.generateUser()
|
||||
expect(appUser[0].builder?.global).toEqual(false)
|
||||
expect(appUser[0].admin?.global).toEqual(false)
|
||||
const [userResponse, userJson] = await config.users.addMultiple(appUser)
|
||||
const [userInfoResponse, userInfoJson] = await config.users.getInfo(
|
||||
const [userResponse, userJson] = await config.api.users.addMultiple(appUser)
|
||||
const [userInfoResponse, userInfoJson] = await config.api.users.getInfo(
|
||||
userJson.created.successful[0]._id
|
||||
)
|
||||
const body: User = {
|
||||
|
@ -85,10 +80,10 @@ describe("Internal API - User Management & Permissions", () => {
|
|||
global: true,
|
||||
},
|
||||
}
|
||||
await config.users.updateInfo(body)
|
||||
await config.api.users.updateInfo(body)
|
||||
|
||||
const [changedUserInfoResponse, changedUserInfoJson] =
|
||||
await config.users.getInfo(userJson.created.successful[0]._id)
|
||||
await config.api.users.getInfo(userJson.created.successful[0]._id)
|
||||
expect(changedUserInfoJson.builder?.global).toBeDefined()
|
||||
expect(changedUserInfoJson.builder?.global).toEqual(true)
|
||||
})
|
|
@ -0,0 +1,78 @@
|
|||
import { AccountInternalAPI } from "../account-api"
|
||||
import * as fixtures from "../internal-api/fixtures"
|
||||
import { BudibaseInternalAPI } from "../internal-api"
|
||||
import { DEFAULT_TENANT_ID, logging } from "@budibase/backend-core"
|
||||
import { CreateAccountRequest } from "@budibase/types"
|
||||
import env from "../environment"
|
||||
import { APIRequestOpts } from "../types"
|
||||
|
||||
// turn off or on context logging i.e. tenantId, appId etc
|
||||
// it's not applicable for the qa run
|
||||
logging.LOG_CONTEXT = false
|
||||
|
||||
const accountsApi = new AccountInternalAPI({})
|
||||
const internalApi = new BudibaseInternalAPI({})
|
||||
|
||||
const API_OPTS: APIRequestOpts = { doExpect: false }
|
||||
|
||||
// @ts-ignore
|
||||
global.qa = {}
|
||||
|
||||
async function createAccount() {
|
||||
const account = fixtures.accounts.generateAccount()
|
||||
await accountsApi.accounts.validateEmail(account.email, API_OPTS)
|
||||
await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS)
|
||||
await accountsApi.accounts.create(account, API_OPTS)
|
||||
return account
|
||||
}
|
||||
|
||||
async function loginAsAdmin() {
|
||||
const username = env.BB_ADMIN_USER_EMAIL!
|
||||
const password = env.BB_ADMIN_USER_PASSWORD!
|
||||
const tenantId = DEFAULT_TENANT_ID
|
||||
const [res, cookie] = await internalApi.auth.login(
|
||||
tenantId,
|
||||
username,
|
||||
password,
|
||||
API_OPTS
|
||||
)
|
||||
|
||||
// @ts-ignore
|
||||
global.qa.authCookie = cookie
|
||||
}
|
||||
|
||||
async function loginAsAccount(account: CreateAccountRequest) {
|
||||
const [res, cookie] = await internalApi.auth.login(
|
||||
account.tenantId,
|
||||
account.email,
|
||||
account.password,
|
||||
API_OPTS
|
||||
)
|
||||
|
||||
// @ts-ignore
|
||||
global.qa.authCookie = cookie
|
||||
}
|
||||
|
||||
async function setup() {
|
||||
console.log("\nGLOBAL SETUP STARTING")
|
||||
const env = await internalApi.environment.getEnvironment(API_OPTS)
|
||||
|
||||
console.log(`Environment: ${JSON.stringify(env)}`)
|
||||
|
||||
if (env.multiTenancy) {
|
||||
const account = await createAccount()
|
||||
// @ts-ignore
|
||||
global.qa.tenantId = account.tenantId
|
||||
await loginAsAccount(account)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
global.qa.tenantId = DEFAULT_TENANT_ID
|
||||
await loginAsAdmin()
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
console.log(`Tenant: ${global.qa.tenantId}`)
|
||||
console.log("GLOBAL SETUP COMPLETE")
|
||||
}
|
||||
|
||||
export default setup
|
|
@ -0,0 +1,9 @@
|
|||
async function teardown() {
|
||||
console.log("\nGLOBAL TEARDOWN STARTING")
|
||||
|
||||
// TODO: Delete account and apps after test run
|
||||
|
||||
console.log("GLOBAL TEARDOWN COMPLETE")
|
||||
}
|
||||
|
||||
export default teardown
|
|
@ -0,0 +1 @@
|
|||
jest.setTimeout(60000)
|
|
@ -0,0 +1,23 @@
|
|||
import AppAPI from "./apis/AppAPI"
|
||||
import UserAPI from "./apis/UserAPI"
|
||||
import TableAPI from "./apis/TableAPI"
|
||||
import RowAPI from "./apis/RowAPI"
|
||||
|
||||
import BudibasePublicAPIClient from "./BudibasePublicAPIClient"
|
||||
import { State } from "../../types"
|
||||
|
||||
export default class BudibasePublicAPI {
|
||||
client: BudibasePublicAPIClient
|
||||
apps: AppAPI
|
||||
users: UserAPI
|
||||
tables: TableAPI
|
||||
rows: RowAPI
|
||||
|
||||
constructor(state: State) {
|
||||
this.client = new BudibasePublicAPIClient(state)
|
||||
this.apps = new AppAPI(this.client)
|
||||
this.users = new UserAPI(this.client)
|
||||
this.tables = new TableAPI(this.client)
|
||||
this.rows = new RowAPI(this.client, state)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import env from "../../environment"
|
||||
import fetch, { HeadersInit } from "node-fetch"
|
||||
import { State } from "../../types"
|
||||
|
||||
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
|
||||
|
||||
interface ApiOptions {
|
||||
method?: APIMethod
|
||||
body?: object
|
||||
headers?: HeadersInit | undefined
|
||||
}
|
||||
|
||||
class BudibasePublicAPIClient {
|
||||
state: State
|
||||
host: string
|
||||
|
||||
constructor(state: State) {
|
||||
if (!env.BUDIBASE_URL) {
|
||||
throw new Error("Must set BUDIBASE_URL env var")
|
||||
}
|
||||
this.host = `${env.BUDIBASE_URL}/api/public/v1`
|
||||
this.state = state
|
||||
}
|
||||
|
||||
apiCall =
|
||||
(method: APIMethod) =>
|
||||
async (url = "", options: ApiOptions = {}) => {
|
||||
const requestOptions = {
|
||||
method,
|
||||
body: JSON.stringify(options.body),
|
||||
headers: {
|
||||
"x-budibase-api-key": this.state.apiKey,
|
||||
"x-budibase-app-id": this.state.appId,
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
...options.headers,
|
||||
redirect: "follow",
|
||||
follow: 20,
|
||||
},
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
// @ts-ignore
|
||||
const response = await fetch(`${this.host}${url}`, requestOptions)
|
||||
|
||||
let body: any
|
||||
const contentType = response.headers.get("content-type")
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
body = await response.json()
|
||||
} else {
|
||||
body = await response.text()
|
||||
}
|
||||
|
||||
const message = `${method} ${url} - ${response.status}
|
||||
Response body: ${JSON.stringify(body)}
|
||||
Request body: ${requestOptions.body}`
|
||||
|
||||
if (response.status > 499) {
|
||||
console.error(message)
|
||||
} else if (response.status >= 400) {
|
||||
console.warn(message)
|
||||
}
|
||||
|
||||
return [response, body]
|
||||
}
|
||||
|
||||
post = this.apiCall("POST")
|
||||
get = this.apiCall("GET")
|
||||
patch = this.apiCall("PATCH")
|
||||
del = this.apiCall("DELETE")
|
||||
put = this.apiCall("PUT")
|
||||
}
|
||||
|
||||
export default BudibasePublicAPIClient
|
|
@ -0,0 +1,68 @@
|
|||
import { Response } from "node-fetch"
|
||||
import BudibasePublicAPIClient from "../BudibasePublicAPIClient"
|
||||
import * as fixtures from "../../fixtures"
|
||||
import {
|
||||
Application,
|
||||
SearchInputParams,
|
||||
CreateApplicationParams,
|
||||
} from "../../../types"
|
||||
|
||||
export default class AppAPI {
|
||||
client: BudibasePublicAPIClient
|
||||
|
||||
constructor(client: BudibasePublicAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async seed(): Promise<[Response, Application]> {
|
||||
return this.create(fixtures.apps.generateApp())
|
||||
}
|
||||
|
||||
async create(
|
||||
body: CreateApplicationParams
|
||||
): Promise<[Response, Application]> {
|
||||
const [response, json] = await this.client.post(`/applications`, { body })
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, Application]> {
|
||||
const [response, json] = await this.client.get(`/applications/${id}`)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [Application]]> {
|
||||
const [response, json] = await this.client.post(`/applications/search`, {
|
||||
body,
|
||||
})
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
body: Application
|
||||
): Promise<[Response, Application]> {
|
||||
const [response, json] = await this.client.put(`/applications/${id}`, {
|
||||
body,
|
||||
})
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<[Response, Application]> {
|
||||
const [response, json] = await this.client.del(`/applications/${id}`)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async publish(id: string): Promise<[Response, any]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/applications/${id}/publish`
|
||||
)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async unpublish(id: string): Promise<[Response]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/applications/${id}/unpublish`
|
||||
)
|
||||
return [response]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import {
|
||||
CreateRowParams,
|
||||
Row,
|
||||
SearchInputParams,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { HeadersInit, Response } from "node-fetch"
|
||||
import BudibasePublicAPIClient from "../BudibasePublicAPIClient"
|
||||
import * as fixtures from "../../fixtures"
|
||||
import { State } from "../../../types"
|
||||
|
||||
export default class RowAPI {
|
||||
client: BudibasePublicAPIClient
|
||||
|
||||
headers?: HeadersInit
|
||||
|
||||
state: State
|
||||
|
||||
constructor(client: BudibasePublicAPIClient, state: State) {
|
||||
this.state = state
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async seed(tableId: string) {
|
||||
return this.create(fixtures.rows.generateRow({ tableId }))
|
||||
}
|
||||
|
||||
async create(body: CreateRowParams): Promise<[Response, Row]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/tables/${this.state.tableId}/rows`,
|
||||
{
|
||||
body,
|
||||
}
|
||||
)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, Row]> {
|
||||
const [response, json] = await this.client.get(
|
||||
`/tables/${this.state.tableId}/rows/${id}`
|
||||
)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [Row]]> {
|
||||
const [response, json] = await this.client.post(
|
||||
`/tables/${this.state.tableId}/rows/search`,
|
||||
{ body }
|
||||
)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(id: string, body: Row): Promise<[Response, Row]> {
|
||||
const [response, json] = await this.client.put(
|
||||
`/tables/${this.state.tableId}/rows/${id}`,
|
||||
{
|
||||
body,
|
||||
}
|
||||
)
|
||||
return [response, json.data]
|
||||
}
|
||||
}
|
|
@ -1,18 +1,19 @@
|
|||
import PublicAPIClient from "./PublicAPIClient"
|
||||
import {
|
||||
Table,
|
||||
SearchInputParams,
|
||||
CreateTableParams,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { HeadersInit, Response } from "node-fetch"
|
||||
import { generateTable } from "../fixtures/tables"
|
||||
import { generateTable } from "../../fixtures/tables"
|
||||
import BudibasePublicAPIClient from "../BudibasePublicAPIClient"
|
||||
|
||||
export default class TableApi {
|
||||
api: PublicAPIClient
|
||||
export default class TableAPI {
|
||||
headers?: HeadersInit
|
||||
|
||||
constructor(apiClient: PublicAPIClient) {
|
||||
this.api = apiClient
|
||||
client: BudibasePublicAPIClient
|
||||
|
||||
constructor(client: BudibasePublicAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async seed() {
|
||||
|
@ -20,28 +21,24 @@ export default class TableApi {
|
|||
}
|
||||
|
||||
async create(body: CreateTableParams): Promise<[Response, Table]> {
|
||||
const response = await this.api.post(`/tables`, {
|
||||
const [response, json] = await this.client.post(`/tables`, {
|
||||
body,
|
||||
})
|
||||
const json = await response.json()
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, Table]> {
|
||||
const response = await this.api.get(`/tables/${id}`)
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.get(`/tables/${id}`)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [Table]]> {
|
||||
const response = await this.api.post(`/tables/search`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.post(`/tables/search`, { body })
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(id: string, body: Table): Promise<[Response, Table]> {
|
||||
const response = await this.api.put(`/tables/${id}`, { body })
|
||||
const json = await response.json()
|
||||
const [response, json] = await this.client.put(`/tables/${id}`, { body })
|
||||
return [response, json.data]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import {
|
||||
CreateUserParams,
|
||||
SearchInputParams,
|
||||
User,
|
||||
} from "@budibase/server/api/controllers/public/mapping/types"
|
||||
import { Response } from "node-fetch"
|
||||
import BudibasePublicAPIClient from "../BudibasePublicAPIClient"
|
||||
import * as fixtures from "../../fixtures"
|
||||
|
||||
export default class UserAPI {
|
||||
client: BudibasePublicAPIClient
|
||||
|
||||
constructor(client: BudibasePublicAPIClient) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async seed() {
|
||||
return this.create(fixtures.users.generateUser())
|
||||
}
|
||||
|
||||
async create(body: CreateUserParams): Promise<[Response, User]> {
|
||||
const [response, json] = await this.client.post(`/users`, { body })
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async read(id: string): Promise<[Response, User]> {
|
||||
const [response, json] = await this.client.get(`/users/${id}`)
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async search(body: SearchInputParams): Promise<[Response, [User]]> {
|
||||
const [response, json] = await this.client.post(`/users/search`, { body })
|
||||
return [response, json.data]
|
||||
}
|
||||
|
||||
async update(id: string, body: User): Promise<[Response, User]> {
|
||||
const [response, json] = await this.client.put(`/users/${id}`, { body })
|
||||
return [response, json.data]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export { default as BudibasePublicAPI } from "./BudibasePublicAPI"
|
|
@ -0,0 +1,33 @@
|
|||
import { BudibasePublicAPI } from "../api"
|
||||
import { BudibaseTestConfiguration } from "../../shared"
|
||||
|
||||
export default class TestConfiguration<T> extends BudibaseTestConfiguration {
|
||||
// apis
|
||||
api: BudibasePublicAPI
|
||||
|
||||
context: T
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.api = new BudibasePublicAPI(this.state)
|
||||
this.context = <T>{}
|
||||
}
|
||||
|
||||
// LIFECYCLE
|
||||
|
||||
async beforeAll() {
|
||||
await super.beforeAll()
|
||||
await this.setApiKey()
|
||||
}
|
||||
|
||||
async afterAll() {
|
||||
await super.afterAll()
|
||||
}
|
||||
|
||||
// AUTH
|
||||
|
||||
async setApiKey() {
|
||||
const apiKeyResponse = await this.internalApi.self.getApiKey()
|
||||
this.state.apiKey = apiKeyResponse.apiKey
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue