Modernise datasource.spec.ts.
This commit is contained in:
parent
f2e3789ad2
commit
c07d73beaf
3 changed files with 288 additions and 378 deletions
|
@ -1,91 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`/datasources fetch returns all the datasources from the server 1`] = `
|
||||
[
|
||||
{
|
||||
"config": {},
|
||||
"entities": [
|
||||
{
|
||||
"_id": "ta_users",
|
||||
"_rev": "1-73b7912e6cbdd3d696febc60f3715844",
|
||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||
"name": "Users",
|
||||
"primaryDisplay": "email",
|
||||
"schema": {
|
||||
"email": {
|
||||
"constraints": {
|
||||
"email": true,
|
||||
"length": {
|
||||
"maximum": "",
|
||||
},
|
||||
"presence": true,
|
||||
"type": "string",
|
||||
},
|
||||
"name": "email",
|
||||
"type": "string",
|
||||
},
|
||||
"firstName": {
|
||||
"constraints": {
|
||||
"presence": false,
|
||||
"type": "string",
|
||||
},
|
||||
"name": "firstName",
|
||||
"type": "string",
|
||||
},
|
||||
"lastName": {
|
||||
"constraints": {
|
||||
"presence": false,
|
||||
"type": "string",
|
||||
},
|
||||
"name": "lastName",
|
||||
"type": "string",
|
||||
},
|
||||
"roleId": {
|
||||
"constraints": {
|
||||
"inclusion": [
|
||||
"ADMIN",
|
||||
"POWER",
|
||||
"BASIC",
|
||||
"PUBLIC",
|
||||
],
|
||||
"presence": false,
|
||||
"type": "string",
|
||||
},
|
||||
"name": "roleId",
|
||||
"type": "options",
|
||||
},
|
||||
"status": {
|
||||
"constraints": {
|
||||
"inclusion": [
|
||||
"active",
|
||||
"inactive",
|
||||
],
|
||||
"presence": false,
|
||||
"type": "string",
|
||||
},
|
||||
"name": "status",
|
||||
"type": "options",
|
||||
},
|
||||
},
|
||||
"sourceId": "bb_internal",
|
||||
"sourceType": "internal",
|
||||
"type": "table",
|
||||
"updatedAt": "2020-01-01T00:00:00.000Z",
|
||||
"views": {},
|
||||
},
|
||||
],
|
||||
"name": "Budibase DB",
|
||||
"source": "BUDIBASE",
|
||||
"type": "budibase",
|
||||
},
|
||||
{
|
||||
"config": {},
|
||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||
"isSQL": true,
|
||||
"name": "Test",
|
||||
"source": "POSTGRES",
|
||||
"type": "datasource",
|
||||
"updatedAt": "2020-01-01T00:00:00.000Z",
|
||||
},
|
||||
]
|
||||
`;
|
|
@ -4,14 +4,12 @@ import { getCachedVariable } from "../../../threads/utils"
|
|||
import { context, events } from "@budibase/backend-core"
|
||||
import sdk from "../../../sdk"
|
||||
|
||||
import tk from "timekeeper"
|
||||
import { mocks } from "@budibase/backend-core/tests"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
import {
|
||||
Datasource,
|
||||
FieldSchema,
|
||||
BBReferenceFieldSubType,
|
||||
FieldType,
|
||||
QueryPreview,
|
||||
RelationshipType,
|
||||
SourceName,
|
||||
Table,
|
||||
|
@ -21,36 +19,34 @@ import {
|
|||
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
||||
import { tableForDatasource } from "../../../tests/utilities/structures"
|
||||
|
||||
tk.freeze(mocks.date.MOCK_DATE)
|
||||
|
||||
let { basicDatasource } = setup.structures
|
||||
|
||||
describe("/datasources", () => {
|
||||
let request = setup.getRequest()
|
||||
let config = setup.getConfig()
|
||||
let datasource: any
|
||||
const config = setup.getConfig()
|
||||
let datasource: Datasource
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.init()
|
||||
})
|
||||
afterAll(setup.afterAll)
|
||||
|
||||
async function setupTest() {
|
||||
await config.init()
|
||||
datasource = await config.createDatasource()
|
||||
beforeEach(async () => {
|
||||
datasource = await config.api.datasource.create({
|
||||
type: "datasource",
|
||||
name: "Test",
|
||||
source: SourceName.POSTGRES,
|
||||
config: {},
|
||||
})
|
||||
jest.clearAllMocks()
|
||||
}
|
||||
|
||||
beforeAll(setupTest)
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
it("should create a new datasource", async () => {
|
||||
const res = await request
|
||||
.post(`/api/datasources`)
|
||||
.send(basicDatasource())
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.datasource.name).toEqual("Test")
|
||||
expect(res.body.errors).toEqual({})
|
||||
const ds = await config.api.datasource.create({
|
||||
type: "datasource",
|
||||
name: "Test",
|
||||
source: SourceName.POSTGRES,
|
||||
config: {},
|
||||
})
|
||||
expect(ds.name).toEqual("Test")
|
||||
expect(events.datasource.created).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
|
@ -72,88 +68,71 @@ describe("/datasources", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("update", () => {
|
||||
it("should update an existing datasource", async () => {
|
||||
datasource.name = "Updated Test"
|
||||
const res = await request
|
||||
.put(`/api/datasources/${datasource._id}`)
|
||||
.send(datasource)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.datasource.name).toEqual("Updated Test")
|
||||
expect(res.body.errors).toBeUndefined()
|
||||
expect(events.datasource.updated).toHaveBeenCalledTimes(1)
|
||||
describe("dynamic variables", () => {
|
||||
it("should invalidate changed or removed variables", async () => {
|
||||
let datasource = await config.api.datasource.create({
|
||||
type: "datasource",
|
||||
name: "Rest",
|
||||
source: SourceName.REST,
|
||||
config: {},
|
||||
})
|
||||
|
||||
describe("dynamic variables", () => {
|
||||
async function preview(
|
||||
datasource: any,
|
||||
fields: { path: string; queryString: string }
|
||||
) {
|
||||
const queryPreview: QueryPreview = {
|
||||
fields,
|
||||
datasourceId: datasource._id,
|
||||
const query = await config.api.query.save({
|
||||
datasourceId: datasource._id!,
|
||||
fields: {
|
||||
path: "www.google.com",
|
||||
},
|
||||
parameters: [],
|
||||
transformer: null,
|
||||
queryVerb: "read",
|
||||
name: datasource.name,
|
||||
name: datasource.name!,
|
||||
schema: {},
|
||||
readable: true,
|
||||
}
|
||||
return config.api.query.preview(queryPreview)
|
||||
}
|
||||
})
|
||||
|
||||
datasource = await config.api.datasource.update({
|
||||
...datasource,
|
||||
config: {
|
||||
dynamicVariables: [
|
||||
{
|
||||
queryId: query._id,
|
||||
name: "variable3",
|
||||
value: "{{ data.0.[value] }}",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
it("should invalidate changed or removed variables", async () => {
|
||||
const { datasource, query } = await config.dynamicVariableDatasource()
|
||||
// preview once to cache variables
|
||||
await preview(datasource, {
|
||||
await config.api.query.preview({
|
||||
fields: {
|
||||
path: "www.example.com",
|
||||
queryString: "test={{ variable3 }}",
|
||||
},
|
||||
datasourceId: datasource._id!,
|
||||
parameters: [],
|
||||
transformer: null,
|
||||
queryVerb: "read",
|
||||
name: datasource.name!,
|
||||
schema: {},
|
||||
readable: true,
|
||||
})
|
||||
|
||||
// check variables in cache
|
||||
let contents = await getCachedVariable(query._id!, "variable3")
|
||||
expect(contents.rows.length).toEqual(1)
|
||||
|
||||
// update the datasource to remove the variables
|
||||
datasource.config!.dynamicVariables = []
|
||||
const res = await request
|
||||
.put(`/api/datasources/${datasource._id}`)
|
||||
.send(datasource)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
expect(res.body.errors).toBeUndefined()
|
||||
await config.api.datasource.update(datasource)
|
||||
|
||||
// check variables no longer in cache
|
||||
contents = await getCachedVariable(query._id!, "variable3")
|
||||
expect(contents).toBe(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("fetch", () => {
|
||||
beforeAll(setupTest)
|
||||
|
||||
it("returns all the datasources from the server", async () => {
|
||||
const res = await request
|
||||
.get(`/api/datasources`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
const datasources = res.body
|
||||
|
||||
// remove non-deterministic fields
|
||||
for (let source of datasources) {
|
||||
delete source._id
|
||||
delete source._rev
|
||||
}
|
||||
|
||||
expect(datasources).toMatchSnapshot()
|
||||
})
|
||||
|
||||
describe("permissions", () => {
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
await checkBuilderEndpoint({
|
||||
config,
|
||||
|
@ -161,41 +140,8 @@ describe("/datasources", () => {
|
|||
url: `/api/datasources`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("find", () => {
|
||||
it("should be able to find a datasource", async () => {
|
||||
const res = await request
|
||||
.get(`/api/datasources/${datasource._id}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
expect(res.body._rev).toBeDefined()
|
||||
expect(res.body._id).toEqual(datasource._id)
|
||||
})
|
||||
})
|
||||
|
||||
describe("destroy", () => {
|
||||
beforeAll(setupTest)
|
||||
|
||||
it("deletes queries for the datasource after deletion and returns a success message", async () => {
|
||||
await config.createQuery()
|
||||
|
||||
await request
|
||||
.delete(`/api/datasources/${datasource._id}/${datasource._rev}`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect(200)
|
||||
|
||||
const res = await request
|
||||
.get(`/api/datasources`)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
|
||||
expect(res.body.length).toEqual(1)
|
||||
expect(events.datasource.deleted).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should apply authorization to endpoint", async () => {
|
||||
it("should apply authorization to delete endpoint", async () => {
|
||||
await checkBuilderEndpoint({
|
||||
config,
|
||||
method: "DELETE",
|
||||
|
@ -204,49 +150,99 @@ describe("/datasources", () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe("check secret replacement", () => {
|
||||
async function makeDatasource() {
|
||||
datasource = basicDatasource()
|
||||
datasource.datasource.config.password = "testing"
|
||||
const res = await request
|
||||
.post(`/api/datasources`)
|
||||
.send(datasource)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
return res.body.datasource
|
||||
}
|
||||
|
||||
it("should save a datasource with password", async () => {
|
||||
const datasource = await makeDatasource()
|
||||
expect(datasource.config.password).toBe("--secret-value--")
|
||||
})
|
||||
|
||||
it("should not the password on update with the --secret-value--", async () => {
|
||||
const datasource = await makeDatasource()
|
||||
await request
|
||||
.put(`/api/datasources/${datasource._id}`)
|
||||
.send(datasource)
|
||||
.set(config.defaultHeaders())
|
||||
.expect("Content-Type", /json/)
|
||||
.expect(200)
|
||||
await context.doInAppContext(config.getAppId(), async () => {
|
||||
const dbDatasource: any = await sdk.datasources.get(datasource._id)
|
||||
expect(dbDatasource.config.password).toBe("testing")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe.each([
|
||||
[DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)],
|
||||
[DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)],
|
||||
[DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)],
|
||||
[DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)],
|
||||
])("fetch schema (%s)", (_, dsProvider) => {
|
||||
beforeAll(async () => {
|
||||
])("%s", (_, dsProvider) => {
|
||||
beforeEach(async () => {
|
||||
datasource = await config.api.datasource.create(await dsProvider)
|
||||
})
|
||||
|
||||
describe("get", () => {
|
||||
it("should be able to get a datasource", async () => {
|
||||
const ds = await config.api.datasource.get(datasource._id!)
|
||||
expect(ds._id).toEqual(datasource._id)
|
||||
expect(ds._rev).toBeDefined()
|
||||
})
|
||||
|
||||
it("should not return database password", async () => {
|
||||
const ds = await config.api.datasource.get(datasource._id!)
|
||||
expect(ds.config!.password).toBe("--secret-value--")
|
||||
})
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
it("returns all the datasources", async () => {
|
||||
const datasources = await config.api.datasource.fetch()
|
||||
expect(datasources).toContainEqual(expect.objectContaining(datasource))
|
||||
})
|
||||
})
|
||||
|
||||
describe("put", () => {
|
||||
it("should update an existing datasource", async () => {
|
||||
const newName = generator.guid()
|
||||
datasource.name = newName
|
||||
const updatedDs = await config.api.datasource.update(datasource)
|
||||
expect(updatedDs.name).toEqual(newName)
|
||||
expect(events.datasource.updated).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it("should not overwrite database password with --secret-value--", async () => {
|
||||
const password = await context.doInAppContext(
|
||||
config.getAppId(),
|
||||
async () => {
|
||||
const ds = await sdk.datasources.get(datasource._id!)
|
||||
return ds.config!.password
|
||||
}
|
||||
)
|
||||
|
||||
expect(password).not.toBe("--secret-value--")
|
||||
|
||||
const ds = await config.api.datasource.get(datasource._id!)
|
||||
expect(ds.config!.password).toBe("--secret-value--")
|
||||
|
||||
await config.api.datasource.update(
|
||||
await config.api.datasource.get(datasource._id!)
|
||||
)
|
||||
|
||||
const newPassword = await context.doInAppContext(
|
||||
config.getAppId(),
|
||||
async () => {
|
||||
const ds = await sdk.datasources.get(datasource._id!)
|
||||
return ds.config!.password
|
||||
}
|
||||
)
|
||||
|
||||
expect(newPassword).not.toBe("--secret-value--")
|
||||
expect(newPassword).toBe(password)
|
||||
})
|
||||
})
|
||||
|
||||
describe("destroy", () => {
|
||||
it("deletes queries for the datasource after deletion and returns a success message", async () => {
|
||||
await config.api.query.save({
|
||||
datasourceId: datasource._id!,
|
||||
name: "Test Query",
|
||||
parameters: [],
|
||||
fields: {},
|
||||
schema: {},
|
||||
queryVerb: "read",
|
||||
transformer: null,
|
||||
readable: true,
|
||||
})
|
||||
|
||||
await config.api.datasource.delete(datasource)
|
||||
const datasources = await config.api.datasource.fetch()
|
||||
expect(datasources).not.toContainEqual(
|
||||
expect.objectContaining(datasource)
|
||||
)
|
||||
expect(events.datasource.deleted).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("schema", () => {
|
||||
it("fetching schema will not drop tables or columns", async () => {
|
||||
const datasourceId = datasource!._id!
|
||||
|
||||
|
@ -376,3 +372,4 @@ describe("/datasources", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -61,6 +61,10 @@ export class DatasourceAPI extends TestAPI {
|
|||
})
|
||||
}
|
||||
|
||||
fetch = async (expectations?: Expectations) => {
|
||||
return await this._get<Datasource[]>(`/api/datasources`, { expectations })
|
||||
}
|
||||
|
||||
query = async (
|
||||
query: Omit<QueryJson, "meta"> & Partial<Pick<QueryJson, "meta">>,
|
||||
expectations?: Expectations
|
||||
|
|
Loading…
Reference in a new issue