diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte
index 11beefff63..dbe289caba 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte
@@ -1,6 +1,7 @@
-
+
diff --git a/packages/builder/src/components/integration/QueryFieldsBuilder.svelte b/packages/builder/src/components/integration/QueryFieldsBuilder.svelte
index 4638a8bc36..24916ef170 100644
--- a/packages/builder/src/components/integration/QueryFieldsBuilder.svelte
+++ b/packages/builder/src/components/integration/QueryFieldsBuilder.svelte
@@ -5,7 +5,6 @@
Label,
Input,
Heading,
- Spacer,
Select
} from "@budibase/bbui"
@@ -13,53 +12,62 @@
export let schema
export let editable
- let customSchema = {}
let draftField = {}
+ $: fieldKeys = Object.keys(fields)
+ $: schemaKeys = Object.keys(schema.fields)
+
+ $: console.log({ fields, schema })
+
function addField() {
// Add the new field to custom fields for the query
- customSchema[draftField.name] = {
+ schema.fields[draftField.name] = {
type: draftField.type
}
// reset the draft field
draftField = {}
}
+
+ function removeField(field) {
+ delete fields[field]
+ fields = fields
+
+ delete schema.fields[field]
+ schema = schema
+ }
+{#if schema.customisable && editable}
+
+
+
+
+
+
+
+
+
+
+{/if}
+
\ No newline at end of file
diff --git a/packages/builder/src/components/integration/SvelteEditor.svelte b/packages/builder/src/components/integration/SvelteEditor.svelte
deleted file mode 100644
index 22cddb5606..0000000000
--- a/packages/builder/src/components/integration/SvelteEditor.svelte
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-
-
-
diff --git a/packages/builder/src/components/integration/index.svelte b/packages/builder/src/components/integration/index.svelte
index 34f0ad9f11..455f2fef40 100644
--- a/packages/builder/src/components/integration/index.svelte
+++ b/packages/builder/src/components/integration/index.svelte
@@ -1,7 +1,7 @@
diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js
index 7cd530121d..8fc3efc8a9 100644
--- a/packages/server/src/api/controllers/datasource.js
+++ b/packages/server/src/api/controllers/datasource.js
@@ -32,7 +32,7 @@ exports.save = async function(ctx) {
datasource._rev = response.rev
ctx.status = 200
- ctx.message = "Datasource created successfully."
+ ctx.message = "Datasource saved successfully."
ctx.body = datasource
} catch (err) {
ctx.throw(err.status, err)
diff --git a/packages/server/src/api/routes/tests/datasource.spec.js b/packages/server/src/api/routes/tests/datasource.spec.js
new file mode 100644
index 0000000000..b699710143
--- /dev/null
+++ b/packages/server/src/api/routes/tests/datasource.spec.js
@@ -0,0 +1,149 @@
+const {
+ supertest,
+ createApplication,
+ defaultHeaders,
+ builderEndpointShouldBlockNormalUsers,
+ getDocument,
+ insertDocument
+} = require("./couchTestUtils")
+let { generateDatasourceID, generateQueryID } = require("../../../db/utils")
+
+const DATASOURCE_ID = generateDatasourceID()
+const TEST_DATASOURCE = {
+ _id: DATASOURCE_ID,
+ type: "datasource",
+ name: "Test",
+ source: "POSTGRES",
+ config: {},
+ type: "datasource",
+}
+
+const TEST_QUERY = {
+ _id: generateQueryID(DATASOURCE_ID),
+ datasourceId: DATASOURCE_ID,
+ name:"New Query",
+ parameters:[],
+ fields:{},
+ schema:{},
+ queryVerb:"read",
+ queryType:"Table",
+}
+
+describe("/datasources", () => {
+ let request
+ let server
+ let app
+ let appId
+ let datasource
+
+ beforeAll(async () => {
+ ({ request, server } = await supertest())
+ });
+
+ afterAll(() => {
+ server.close()
+ })
+
+ beforeEach(async () => {
+ app = await createApplication(request)
+ appId = app.instance._id
+ });
+
+ async function createDatasource() {
+ return await insertDocument(appId, TEST_DATASOURCE)
+ }
+
+ async function createQuery() {
+ return await insertDocument(appId, TEST_QUERY)
+ }
+
+ describe("create", () => {
+ it("should create a new datasource", async () => {
+ const res = await request
+ .post(`/api/datasources`)
+ .send(TEST_DATASOURCE)
+ .set(defaultHeaders(appId))
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ expect(res.res.statusMessage).toEqual("Datasource saved successfully.");
+ expect(res.body.name).toEqual("Test");
+ })
+ });
+
+ describe("fetch", () => {
+ let datasource
+
+ beforeEach(async () => {
+ datasource = await createDatasource()
+ });
+
+ afterEach(() => {
+ delete datasource._rev
+ });
+
+ it("returns all the datasources from the server", async () => {
+ const res = await request
+ .get(`/api/datasources`)
+ .set(defaultHeaders(appId))
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ const datasources = res.body;
+ expect(datasources).toEqual([
+ {
+ "_rev": datasources[0]._rev,
+ ...TEST_DATASOURCE
+ }
+ ]);
+ })
+
+ it("should apply authorization to endpoint", async () => {
+ await builderEndpointShouldBlockNormalUsers({
+ request,
+ method: "GET",
+ url: `/api/datasources`,
+ appId: appId,
+ })
+ })
+ });
+
+ describe("destroy", () => {
+ let datasource;
+
+ beforeEach(async () => {
+ datasource = await createDatasource()
+ });
+
+ afterEach(() => {
+ delete datasource._rev
+ });
+
+ it("deletes queries for the datasource after deletion and returns a success message", async () => {
+ await createQuery(datasource.id)
+
+ await request
+ .delete(`/api/datasources/${datasource.id}/${datasource.rev}`)
+ .set(defaultHeaders(appId))
+ .expect(200)
+
+ const res = await request
+ .get(`/api/datasources`)
+ .set(defaultHeaders(appId))
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ expect(res.body).toEqual([])
+ })
+
+ it("should apply authorization to endpoint", async () => {
+ await builderEndpointShouldBlockNormalUsers({
+ request,
+ method: "DELETE",
+ url: `/api/datasources/${datasource._id}/${datasource._rev}`,
+ appId: appId,
+ })
+ })
+
+ });
+});
diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js
new file mode 100644
index 0000000000..f8c31e48e6
--- /dev/null
+++ b/packages/server/src/api/routes/tests/query.spec.js
@@ -0,0 +1,153 @@
+const {
+ supertest,
+ createApplication,
+ defaultHeaders,
+ builderEndpointShouldBlockNormalUsers,
+ getDocument,
+ insertDocument
+} = require("./couchTestUtils")
+let { generateDatasourceID, generateQueryID } = require("../../../db/utils")
+
+const DATASOURCE_ID = generateDatasourceID()
+const TEST_DATASOURCE = {
+ _id: DATASOURCE_ID,
+ type: "datasource",
+ name: "Test",
+ source: "POSTGRES",
+ config: {},
+ type: "datasource",
+}
+
+const TEST_QUERY = {
+ _id: generateQueryID(DATASOURCE_ID),
+ datasourceId: DATASOURCE_ID,
+ name:"New Query",
+ parameters:[],
+ fields:{},
+ schema:{},
+ queryVerb:"read",
+ queryType:"Table",
+}
+
+describe("/queries", () => {
+ let request
+ let server
+ let app
+ let appId
+ let datasource
+ let query
+
+ beforeAll(async () => {
+ ({ request, server } = await supertest())
+ });
+
+ afterAll(() => {
+ server.close()
+ })
+
+ beforeEach(async () => {
+ app = await createApplication(request)
+ appId = app.instance._id
+ });
+
+ async function createDatasource() {
+ return await insertDocument(appId, TEST_DATASOURCE)
+ }
+
+ async function createQuery() {
+ return await insertDocument(appId, TEST_QUERY)
+ }
+
+ describe("create", () => {
+ it("should create a new query", async () => {
+ const res = await request
+ .post(`/api/queries`)
+ .send(TEST_QUERY)
+ .set(defaultHeaders(appId))
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ expect(res.res.statusMessage).toEqual(`Query ${TEST_QUERY.name} saved successfully.`);
+ expect(res.body).toEqual({
+ _rev: res.body._rev,
+ ...TEST_QUERY,
+ });
+ })
+ });
+
+ describe("fetch", () => {
+ let datasource
+
+ beforeEach(async () => {
+ datasource = await createDatasource()
+ });
+
+ afterEach(() => {
+ delete datasource._rev
+ });
+
+ it("returns all the queries from the server", async () => {
+ const query = await createQuery()
+ const res = await request
+ .get(`/api/queries`)
+ .set(defaultHeaders(appId))
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ const queries = res.body;
+ expect(queries).toEqual([
+ {
+ "_rev": query.rev,
+ ...TEST_QUERY
+ }
+ ]);
+ })
+
+ it("should apply authorization to endpoint", async () => {
+ await builderEndpointShouldBlockNormalUsers({
+ request,
+ method: "GET",
+ url: `/api/datasources`,
+ appId: appId,
+ })
+ })
+ });
+
+ describe("destroy", () => {
+ let datasource;
+
+ beforeEach(async () => {
+ datasource = await createDatasource()
+ });
+
+ afterEach(() => {
+ delete datasource._rev
+ });
+
+ it("deletes a query and returns a success message", async () => {
+ const query = await createQuery()
+
+ await request
+ .delete(`/api/queries/${query.id}/${query.rev}`)
+ .set(defaultHeaders(appId))
+ .expect(200)
+
+ const res = await request
+ .get(`/api/queries`)
+ .set(defaultHeaders(appId))
+ .expect('Content-Type', /json/)
+ .expect(200)
+
+ expect(res.body).toEqual([])
+ })
+
+ it("should apply authorization to endpoint", async () => {
+ await builderEndpointShouldBlockNormalUsers({
+ request,
+ method: "DELETE",
+ url: `/api/datasources/${datasource._id}/${datasource._rev}`,
+ appId: appId,
+ })
+ })
+ });
+});