Revert "Dynamic schema generation for query arrays: bug fix and refactor (#13…"
This reverts commit 0c9d13c79a
.
This commit is contained in:
parent
94f7d385e1
commit
ae4bc362c3
|
@ -75,7 +75,17 @@ export function createQueriesStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const preview = async query => {
|
const preview = async query => {
|
||||||
const result = await API.previewQuery(query)
|
const parameters = query.parameters.reduce(
|
||||||
|
(acc, next) => ({
|
||||||
|
...acc,
|
||||||
|
[next.name]: next.default,
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
const result = await API.previewQuery({
|
||||||
|
...query,
|
||||||
|
parameters,
|
||||||
|
})
|
||||||
// Assume all the fields are strings and create a basic schema from the
|
// Assume all the fields are strings and create a basic schema from the
|
||||||
// unique fields returned by the server
|
// unique fields returned by the server
|
||||||
const schema = {}
|
const schema = {}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
type ExecuteQueryRequest,
|
type ExecuteQueryRequest,
|
||||||
type ExecuteQueryResponse,
|
type ExecuteQueryResponse,
|
||||||
type Row,
|
type Row,
|
||||||
QueryParameter,
|
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { ValidQueryNameRegex, utils as JsonUtils } from "@budibase/shared-core"
|
import { ValidQueryNameRegex, utils as JsonUtils } from "@budibase/shared-core"
|
||||||
|
|
||||||
|
@ -119,21 +118,6 @@ function getAuthConfig(ctx: UserCtx) {
|
||||||
return authConfigCtx
|
return authConfigCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
function enrichParameters(
|
|
||||||
queryParameters: QueryParameter[],
|
|
||||||
requestParameters: { [key: string]: string } = {}
|
|
||||||
): {
|
|
||||||
[key: string]: string
|
|
||||||
} {
|
|
||||||
// make sure parameters are fully enriched with defaults
|
|
||||||
for (let parameter of queryParameters) {
|
|
||||||
if (!requestParameters[parameter.name]) {
|
|
||||||
requestParameters[parameter.name] = parameter.default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return requestParameters
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function preview(ctx: UserCtx) {
|
export async function preview(ctx: UserCtx) {
|
||||||
const { datasource, envVars } = await sdk.datasources.getWithEnvVars(
|
const { datasource, envVars } = await sdk.datasources.getWithEnvVars(
|
||||||
ctx.request.body.datasourceId
|
ctx.request.body.datasourceId
|
||||||
|
@ -158,68 +142,6 @@ export async function preview(ctx: UserCtx) {
|
||||||
|
|
||||||
const authConfigCtx: any = getAuthConfig(ctx)
|
const authConfigCtx: any = getAuthConfig(ctx)
|
||||||
|
|
||||||
function getFieldMetadata(field: any, key: string): QuerySchema {
|
|
||||||
const makeQuerySchema = (
|
|
||||||
type: FieldType,
|
|
||||||
name: string,
|
|
||||||
subtype?: string
|
|
||||||
): QuerySchema => ({
|
|
||||||
type,
|
|
||||||
name,
|
|
||||||
subtype,
|
|
||||||
})
|
|
||||||
// Because custom queries have no fixed schema, we dynamically determine the schema,
|
|
||||||
// however types cannot be determined from null. We have no 'unknown' type, so we default to string.
|
|
||||||
let type = typeof field,
|
|
||||||
fieldMetadata = makeQuerySchema(FieldType.STRING, key)
|
|
||||||
if (field != null)
|
|
||||||
switch (type) {
|
|
||||||
case "boolean":
|
|
||||||
fieldMetadata = makeQuerySchema(FieldType.BOOLEAN, key)
|
|
||||||
break
|
|
||||||
case "object":
|
|
||||||
if (field instanceof Date) {
|
|
||||||
fieldMetadata = makeQuerySchema(FieldType.DATETIME, key)
|
|
||||||
} else if (Array.isArray(field)) {
|
|
||||||
if (field.some(item => JsonUtils.hasSchema(item))) {
|
|
||||||
fieldMetadata = makeQuerySchema(
|
|
||||||
FieldType.JSON,
|
|
||||||
key,
|
|
||||||
JsonFieldSubType.ARRAY
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
fieldMetadata = makeQuerySchema(FieldType.ARRAY, key)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fieldMetadata = makeQuerySchema(FieldType.JSON, key)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "number":
|
|
||||||
fieldMetadata = makeQuerySchema(FieldType.NUMBER, key)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return fieldMetadata
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildNestedSchema(
|
|
||||||
nestedSchemaFields: {
|
|
||||||
[key: string]: Record<string, string | QuerySchema>
|
|
||||||
},
|
|
||||||
key: string,
|
|
||||||
fieldArray: any[]
|
|
||||||
) {
|
|
||||||
let schema: { [key: string]: any } = {}
|
|
||||||
// build the schema by aggregating all row objects in the array
|
|
||||||
for (const item of fieldArray) {
|
|
||||||
if (JsonUtils.hasSchema(item)) {
|
|
||||||
for (const [key, value] of Object.entries(item)) {
|
|
||||||
schema[key] = getFieldMetadata(value, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nestedSchemaFields[key] = schema
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSchemaFields(
|
function getSchemaFields(
|
||||||
rows: any[],
|
rows: any[],
|
||||||
keys: string[]
|
keys: string[]
|
||||||
|
@ -233,16 +155,51 @@ export async function preview(ctx: UserCtx) {
|
||||||
const nestedSchemaFields: {
|
const nestedSchemaFields: {
|
||||||
[key: string]: Record<string, string | QuerySchema>
|
[key: string]: Record<string, string | QuerySchema>
|
||||||
} = {}
|
} = {}
|
||||||
|
const makeQuerySchema = (
|
||||||
|
type: FieldType,
|
||||||
|
name: string,
|
||||||
|
subtype?: string
|
||||||
|
): QuerySchema => ({
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
subtype,
|
||||||
|
})
|
||||||
if (rows?.length > 0) {
|
if (rows?.length > 0) {
|
||||||
for (let key of new Set(keys)) {
|
for (let key of [...new Set(keys)] as string[]) {
|
||||||
const fieldMetadata = getFieldMetadata(rows[0][key], key)
|
const field = rows[0][key]
|
||||||
previewSchema[key] = fieldMetadata
|
let type = typeof field,
|
||||||
if (
|
fieldMetadata = makeQuerySchema(FieldType.STRING, key)
|
||||||
fieldMetadata.type === FieldType.JSON &&
|
if (field)
|
||||||
fieldMetadata.subtype === JsonFieldSubType.ARRAY
|
switch (type) {
|
||||||
) {
|
case "boolean":
|
||||||
buildNestedSchema(nestedSchemaFields, key, rows[0][key])
|
fieldMetadata = makeQuerySchema(FieldType.BOOLEAN, key)
|
||||||
|
break
|
||||||
|
case "object":
|
||||||
|
if (field instanceof Date) {
|
||||||
|
fieldMetadata = makeQuerySchema(FieldType.DATETIME, key)
|
||||||
|
} else if (Array.isArray(field)) {
|
||||||
|
if (JsonUtils.hasSchema(field[0])) {
|
||||||
|
fieldMetadata = makeQuerySchema(
|
||||||
|
FieldType.JSON,
|
||||||
|
key,
|
||||||
|
JsonFieldSubType.ARRAY
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
fieldMetadata = makeQuerySchema(FieldType.ARRAY, key)
|
||||||
}
|
}
|
||||||
|
nestedSchemaFields[key] = getSchemaFields(
|
||||||
|
field,
|
||||||
|
Object.keys(field[0])
|
||||||
|
).previewSchema
|
||||||
|
} else {
|
||||||
|
fieldMetadata = makeQuerySchema(FieldType.JSON, key)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "number":
|
||||||
|
fieldMetadata = makeQuerySchema(FieldType.NUMBER, key)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
previewSchema[key] = fieldMetadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { previewSchema, nestedSchemaFields }
|
return { previewSchema, nestedSchemaFields }
|
||||||
|
@ -254,7 +211,7 @@ export async function preview(ctx: UserCtx) {
|
||||||
datasource,
|
datasource,
|
||||||
queryVerb,
|
queryVerb,
|
||||||
fields,
|
fields,
|
||||||
parameters: enrichParameters(parameters),
|
parameters,
|
||||||
transformer,
|
transformer,
|
||||||
queryId,
|
queryId,
|
||||||
schema,
|
schema,
|
||||||
|
@ -309,6 +266,15 @@ async function execute(
|
||||||
if (!opts.isAutomation) {
|
if (!opts.isAutomation) {
|
||||||
authConfigCtx = getAuthConfig(ctx)
|
authConfigCtx = getAuthConfig(ctx)
|
||||||
}
|
}
|
||||||
|
const enrichedParameters = ctx.request.body.parameters || {}
|
||||||
|
// make sure parameters are fully enriched with defaults
|
||||||
|
if (query && query.parameters) {
|
||||||
|
for (let parameter of query.parameters) {
|
||||||
|
if (!enrichedParameters[parameter.name]) {
|
||||||
|
enrichedParameters[parameter.name] = parameter.default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// call the relevant CRUD method on the integration class
|
// call the relevant CRUD method on the integration class
|
||||||
try {
|
try {
|
||||||
|
@ -318,10 +284,7 @@ async function execute(
|
||||||
queryVerb: query.queryVerb,
|
queryVerb: query.queryVerb,
|
||||||
fields: query.fields,
|
fields: query.fields,
|
||||||
pagination: ctx.request.body.pagination,
|
pagination: ctx.request.body.pagination,
|
||||||
parameters: enrichParameters(
|
parameters: enrichedParameters,
|
||||||
query.parameters,
|
|
||||||
ctx.request.body.parameters
|
|
||||||
),
|
|
||||||
transformer: query.transformer,
|
transformer: query.transformer,
|
||||||
queryId: ctx.params.queryId,
|
queryId: ctx.params.queryId,
|
||||||
// have to pass down to the thread runner - can't put into context now
|
// have to pass down to the thread runner - can't put into context now
|
||||||
|
|
|
@ -3,10 +3,11 @@ import Joi from "joi"
|
||||||
|
|
||||||
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
|
||||||
|
|
||||||
function baseQueryValidation() {
|
export function queryValidation() {
|
||||||
return {
|
return Joi.object({
|
||||||
_id: OPTIONAL_STRING,
|
_id: Joi.string(),
|
||||||
_rev: OPTIONAL_STRING,
|
_rev: Joi.string(),
|
||||||
|
name: Joi.string().required(),
|
||||||
fields: Joi.object().required(),
|
fields: Joi.object().required(),
|
||||||
datasourceId: Joi.string().required(),
|
datasourceId: Joi.string().required(),
|
||||||
readable: Joi.boolean(),
|
readable: Joi.boolean(),
|
||||||
|
@ -16,19 +17,11 @@ function baseQueryValidation() {
|
||||||
default: Joi.string().allow(""),
|
default: Joi.string().allow(""),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
queryVerb: Joi.string().required(),
|
queryVerb: Joi.string().allow().required(),
|
||||||
extra: Joi.object().optional(),
|
extra: Joi.object().optional(),
|
||||||
schema: Joi.object({}).required().unknown(true),
|
schema: Joi.object({}).required().unknown(true),
|
||||||
transformer: OPTIONAL_STRING,
|
transformer: OPTIONAL_STRING,
|
||||||
flags: Joi.object().optional(),
|
flags: Joi.object().optional(),
|
||||||
queryId: OPTIONAL_STRING,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function queryValidation() {
|
|
||||||
return Joi.object({
|
|
||||||
...baseQueryValidation(),
|
|
||||||
name: Joi.string().required(),
|
|
||||||
}).unknown(true)
|
}).unknown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +32,19 @@ export function generateQueryValidation() {
|
||||||
|
|
||||||
export function generateQueryPreviewValidation() {
|
export function generateQueryPreviewValidation() {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
return auth.joiValidator.body(
|
return auth.joiValidator.body(Joi.object({
|
||||||
Joi.object({
|
_id: OPTIONAL_STRING,
|
||||||
...baseQueryValidation(),
|
_rev: OPTIONAL_STRING,
|
||||||
|
readable: Joi.boolean().optional(),
|
||||||
|
fields: Joi.object().required(),
|
||||||
|
queryVerb: Joi.string().required(),
|
||||||
name: OPTIONAL_STRING,
|
name: OPTIONAL_STRING,
|
||||||
}).unknown(true)
|
flags: Joi.object().optional(),
|
||||||
)
|
schema: Joi.object().optional(),
|
||||||
|
extra: Joi.object().optional(),
|
||||||
|
datasourceId: Joi.string().required(),
|
||||||
|
transformer: OPTIONAL_STRING,
|
||||||
|
parameters: Joi.object({}).required().unknown(true),
|
||||||
|
queryId: OPTIONAL_STRING,
|
||||||
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import {
|
||||||
paramResource,
|
paramResource,
|
||||||
} from "../../middleware/resourceId"
|
} from "../../middleware/resourceId"
|
||||||
import {
|
import {
|
||||||
generateQueryValidation,
|
|
||||||
generateQueryPreviewValidation,
|
generateQueryPreviewValidation,
|
||||||
|
generateQueryValidation,
|
||||||
} from "../controllers/query/validation"
|
} from "../controllers/query/validation"
|
||||||
|
|
||||||
const { BUILDER, PermissionType, PermissionLevel } = permissions
|
const { BUILDER, PermissionType, PermissionLevel } = permissions
|
||||||
|
|
|
@ -7,7 +7,6 @@ import sdk from "../../../sdk"
|
||||||
|
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
import { mocks } from "@budibase/backend-core/tests"
|
import { mocks } from "@budibase/backend-core/tests"
|
||||||
import { QueryPreview } from "@budibase/types"
|
|
||||||
|
|
||||||
tk.freeze(mocks.date.MOCK_DATE)
|
tk.freeze(mocks.date.MOCK_DATE)
|
||||||
|
|
||||||
|
@ -64,17 +63,14 @@ describe("/datasources", () => {
|
||||||
datasource: any,
|
datasource: any,
|
||||||
fields: { path: string; queryString: string }
|
fields: { path: string; queryString: string }
|
||||||
) {
|
) {
|
||||||
const queryPreview: QueryPreview = {
|
return config.previewQuery(
|
||||||
|
request,
|
||||||
|
config,
|
||||||
|
datasource,
|
||||||
fields,
|
fields,
|
||||||
datasourceId: datasource._id,
|
undefined,
|
||||||
parameters: [],
|
""
|
||||||
transformer: null,
|
)
|
||||||
queryVerb: "read",
|
|
||||||
name: datasource.name,
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
|
||||||
return config.api.query.previewQuery(queryPreview)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should invalidate changed or removed variables", async () => {
|
it("should invalidate changed or removed variables", async () => {
|
||||||
|
|
|
@ -14,7 +14,6 @@ jest.mock("pg", () => {
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import { mocks } from "@budibase/backend-core/tests"
|
import { mocks } from "@budibase/backend-core/tests"
|
||||||
import { env, events } from "@budibase/backend-core"
|
import { env, events } from "@budibase/backend-core"
|
||||||
import { QueryPreview } from "@budibase/types"
|
|
||||||
|
|
||||||
const structures = setup.structures
|
const structures = setup.structures
|
||||||
|
|
||||||
|
@ -121,19 +120,16 @@ describe("/api/env/variables", () => {
|
||||||
.expect(200)
|
.expect(200)
|
||||||
expect(response.body.datasource._id).toBeDefined()
|
expect(response.body.datasource._id).toBeDefined()
|
||||||
|
|
||||||
const queryPreview: QueryPreview = {
|
const query = {
|
||||||
datasourceId: response.body.datasource._id,
|
datasourceId: response.body.datasource._id,
|
||||||
parameters: [],
|
parameters: {},
|
||||||
fields: {},
|
fields: {},
|
||||||
queryVerb: "read",
|
queryVerb: "read",
|
||||||
name: response.body.datasource.name,
|
name: response.body.datasource.name,
|
||||||
transformer: null,
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
}
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/queries/preview`)
|
.post(`/api/queries/preview`)
|
||||||
.send(queryPreview)
|
.send(query)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
@ -143,7 +139,7 @@ describe("/api/env/variables", () => {
|
||||||
delete response.body.datasource.config
|
delete response.body.datasource.config
|
||||||
expect(events.query.previewed).toBeCalledWith(
|
expect(events.query.previewed).toBeCalledWith(
|
||||||
response.body.datasource,
|
response.body.datasource,
|
||||||
queryPreview
|
query
|
||||||
)
|
)
|
||||||
expect(pg.Client).toHaveBeenCalledWith({ password: "test", ssl: undefined })
|
expect(pg.Client).toHaveBeenCalledWith({ password: "test", ssl: undefined })
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import tk from "timekeeper"
|
import tk from "timekeeper"
|
||||||
|
|
||||||
const pg = require("pg")
|
|
||||||
|
|
||||||
// Mock out postgres for this
|
// Mock out postgres for this
|
||||||
jest.mock("pg")
|
jest.mock("pg")
|
||||||
jest.mock("node-fetch")
|
jest.mock("node-fetch")
|
||||||
|
@ -24,13 +22,7 @@ import { checkCacheForDynamicVariable } from "../../../../threads/utils"
|
||||||
|
|
||||||
const { basicQuery, basicDatasource } = setup.structures
|
const { basicQuery, basicDatasource } = setup.structures
|
||||||
import { events, db as dbCore } from "@budibase/backend-core"
|
import { events, db as dbCore } from "@budibase/backend-core"
|
||||||
import {
|
import { Datasource, Query, SourceName } from "@budibase/types"
|
||||||
Datasource,
|
|
||||||
Query,
|
|
||||||
SourceName,
|
|
||||||
QueryPreview,
|
|
||||||
QueryParameter,
|
|
||||||
} from "@budibase/types"
|
|
||||||
|
|
||||||
tk.freeze(Date.now())
|
tk.freeze(Date.now())
|
||||||
|
|
||||||
|
@ -226,26 +218,28 @@ describe("/queries", () => {
|
||||||
|
|
||||||
describe("preview", () => {
|
describe("preview", () => {
|
||||||
it("should be able to preview the query", async () => {
|
it("should be able to preview the query", async () => {
|
||||||
const queryPreview: QueryPreview = {
|
const query = {
|
||||||
datasourceId: datasource._id,
|
datasourceId: datasource._id,
|
||||||
queryVerb: "read",
|
parameters: {},
|
||||||
fields: {},
|
fields: {},
|
||||||
parameters: [],
|
queryVerb: "read",
|
||||||
transformer: "return data",
|
name: datasource.name,
|
||||||
name: datasource.name!,
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
}
|
||||||
const responseBody = await config.api.query.previewQuery(queryPreview)
|
const res = await request
|
||||||
|
.post(`/api/queries/preview`)
|
||||||
|
.send(query)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
// these responses come from the mock
|
// these responses come from the mock
|
||||||
expect(responseBody.schema).toEqual({
|
expect(res.body.schema).toEqual({
|
||||||
a: { type: "string", name: "a" },
|
a: { type: "string", name: "a" },
|
||||||
b: { type: "number", name: "b" },
|
b: { type: "number", name: "b" },
|
||||||
})
|
})
|
||||||
expect(responseBody.rows.length).toEqual(1)
|
expect(res.body.rows.length).toEqual(1)
|
||||||
expect(events.query.previewed).toBeCalledTimes(1)
|
expect(events.query.previewed).toBeCalledTimes(1)
|
||||||
delete datasource.config
|
delete datasource.config
|
||||||
expect(events.query.previewed).toBeCalledWith(datasource, queryPreview)
|
expect(events.query.previewed).toBeCalledWith(datasource, query)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
|
@ -255,128 +249,6 @@ describe("/queries", () => {
|
||||||
url: `/api/queries/preview`,
|
url: `/api/queries/preview`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not error when trying to generate a nested schema for an empty array", async () => {
|
|
||||||
const queryPreview: QueryPreview = {
|
|
||||||
datasourceId: datasource._id,
|
|
||||||
parameters: [],
|
|
||||||
fields: {},
|
|
||||||
queryVerb: "read",
|
|
||||||
name: datasource.name!,
|
|
||||||
transformer: "return data",
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
|
||||||
const rows = [
|
|
||||||
{
|
|
||||||
contacts: [],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
pg.queryMock.mockImplementation(() => ({
|
|
||||||
rows,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const responseBody = await config.api.query.previewQuery(queryPreview)
|
|
||||||
expect(responseBody).toEqual({
|
|
||||||
nestedSchemaFields: {},
|
|
||||||
rows,
|
|
||||||
schema: {
|
|
||||||
contacts: { type: "array", name: "contacts" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(responseBody.rows.length).toEqual(1)
|
|
||||||
delete datasource.config
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should generate a nested schema based on all the nested items", async () => {
|
|
||||||
const queryPreview: QueryPreview = {
|
|
||||||
datasourceId: datasource._id,
|
|
||||||
parameters: [],
|
|
||||||
fields: {},
|
|
||||||
queryVerb: "read",
|
|
||||||
name: datasource.name!,
|
|
||||||
transformer: "return data",
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
|
||||||
const rows = [
|
|
||||||
{
|
|
||||||
contacts: [
|
|
||||||
{
|
|
||||||
address: "123 Lane",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address: "456 Drive",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
postcode: "BT1 12N",
|
|
||||||
lat: 54.59,
|
|
||||||
long: -5.92,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
city: "Belfast",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
address: "789 Avenue",
|
|
||||||
phoneNumber: "0800-999-5555",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Name",
|
|
||||||
isActive: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
pg.queryMock.mockImplementation(() => ({
|
|
||||||
rows,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const responseBody = await config.api.query.previewQuery(queryPreview)
|
|
||||||
expect(responseBody).toEqual({
|
|
||||||
nestedSchemaFields: {
|
|
||||||
contacts: {
|
|
||||||
address: {
|
|
||||||
type: "string",
|
|
||||||
name: "address",
|
|
||||||
},
|
|
||||||
postcode: {
|
|
||||||
type: "string",
|
|
||||||
name: "postcode",
|
|
||||||
},
|
|
||||||
lat: {
|
|
||||||
type: "number",
|
|
||||||
name: "lat",
|
|
||||||
},
|
|
||||||
long: {
|
|
||||||
type: "number",
|
|
||||||
name: "long",
|
|
||||||
},
|
|
||||||
city: {
|
|
||||||
type: "string",
|
|
||||||
name: "city",
|
|
||||||
},
|
|
||||||
phoneNumber: {
|
|
||||||
type: "string",
|
|
||||||
name: "phoneNumber",
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: "string",
|
|
||||||
name: "name",
|
|
||||||
},
|
|
||||||
isActive: {
|
|
||||||
type: "boolean",
|
|
||||||
name: "isActive",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
rows,
|
|
||||||
schema: {
|
|
||||||
contacts: { type: "json", name: "contacts", subtype: "array" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(responseBody.rows.length).toEqual(1)
|
|
||||||
delete datasource.config
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("execute", () => {
|
describe("execute", () => {
|
||||||
|
@ -411,17 +283,7 @@ describe("/queries", () => {
|
||||||
|
|
||||||
describe("variables", () => {
|
describe("variables", () => {
|
||||||
async function preview(datasource: Datasource, fields: any) {
|
async function preview(datasource: Datasource, fields: any) {
|
||||||
const queryPreview: QueryPreview = {
|
return config.previewQuery(request, config, datasource, fields, undefined)
|
||||||
datasourceId: datasource._id!,
|
|
||||||
parameters: [],
|
|
||||||
fields,
|
|
||||||
queryVerb: "read",
|
|
||||||
name: datasource.name!,
|
|
||||||
transformer: "return data",
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
|
||||||
return await config.api.query.previewQuery(queryPreview)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should work with static variables", async () => {
|
it("should work with static variables", async () => {
|
||||||
|
@ -431,31 +293,31 @@ describe("/queries", () => {
|
||||||
variable2: "1",
|
variable2: "1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const responseBody = await preview(datasource, {
|
const res = await preview(datasource, {
|
||||||
path: "www.{{ variable }}.com",
|
path: "www.{{ variable }}.com",
|
||||||
queryString: "test={{ variable2 }}",
|
queryString: "test={{ variable2 }}",
|
||||||
})
|
})
|
||||||
// these responses come from the mock
|
// these responses come from the mock
|
||||||
expect(responseBody.schema).toEqual({
|
expect(res.body.schema).toEqual({
|
||||||
opts: { type: "json", name: "opts" },
|
opts: { type: "json", name: "opts" },
|
||||||
url: { type: "string", name: "url" },
|
url: { type: "string", name: "url" },
|
||||||
value: { type: "string", name: "value" },
|
value: { type: "string", name: "value" },
|
||||||
})
|
})
|
||||||
expect(responseBody.rows[0].url).toEqual("http://www.google.com?test=1")
|
expect(res.body.rows[0].url).toEqual("http://www.google.com?test=1")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should work with dynamic variables", async () => {
|
it("should work with dynamic variables", async () => {
|
||||||
const { datasource } = await config.dynamicVariableDatasource()
|
const { datasource } = await config.dynamicVariableDatasource()
|
||||||
const responseBody = await preview(datasource, {
|
const res = await preview(datasource, {
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
queryString: "test={{ variable3 }}",
|
queryString: "test={{ variable3 }}",
|
||||||
})
|
})
|
||||||
expect(responseBody.schema).toEqual({
|
expect(res.body.schema).toEqual({
|
||||||
opts: { type: "json", name: "opts" },
|
opts: { type: "json", name: "opts" },
|
||||||
url: { type: "string", name: "url" },
|
url: { type: "string", name: "url" },
|
||||||
value: { type: "string", name: "value" },
|
value: { type: "string", name: "value" },
|
||||||
})
|
})
|
||||||
expect(responseBody.rows[0].url).toContain("doctype%20html")
|
expect(res.body.rows[0].url).toContain("doctype%20html")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("check that it automatically retries on fail with cached dynamics", async () => {
|
it("check that it automatically retries on fail with cached dynamics", async () => {
|
||||||
|
@ -469,16 +331,16 @@ describe("/queries", () => {
|
||||||
// check its in cache
|
// check its in cache
|
||||||
const contents = await checkCacheForDynamicVariable(base._id, "variable3")
|
const contents = await checkCacheForDynamicVariable(base._id, "variable3")
|
||||||
expect(contents.rows.length).toEqual(1)
|
expect(contents.rows.length).toEqual(1)
|
||||||
const responseBody = await preview(datasource, {
|
const res = await preview(datasource, {
|
||||||
path: "www.failonce.com",
|
path: "www.failonce.com",
|
||||||
queryString: "test={{ variable3 }}",
|
queryString: "test={{ variable3 }}",
|
||||||
})
|
})
|
||||||
expect(responseBody.schema).toEqual({
|
expect(res.body.schema).toEqual({
|
||||||
fails: { type: "number", name: "fails" },
|
fails: { type: "number", name: "fails" },
|
||||||
opts: { type: "json", name: "opts" },
|
opts: { type: "json", name: "opts" },
|
||||||
url: { type: "string", name: "url" },
|
url: { type: "string", name: "url" },
|
||||||
})
|
})
|
||||||
expect(responseBody.rows[0].fails).toEqual(1)
|
expect(res.body.rows[0].fails).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("deletes variables when linked query is deleted", async () => {
|
it("deletes variables when linked query is deleted", async () => {
|
||||||
|
@ -509,37 +371,24 @@ describe("/queries", () => {
|
||||||
async function previewGet(
|
async function previewGet(
|
||||||
datasource: Datasource,
|
datasource: Datasource,
|
||||||
fields: any,
|
fields: any,
|
||||||
params: QueryParameter[]
|
params: any
|
||||||
) {
|
) {
|
||||||
const queryPreview: QueryPreview = {
|
return config.previewQuery(request, config, datasource, fields, params)
|
||||||
datasourceId: datasource._id!,
|
|
||||||
parameters: params,
|
|
||||||
fields,
|
|
||||||
queryVerb: "read",
|
|
||||||
name: datasource.name!,
|
|
||||||
transformer: "return data",
|
|
||||||
schema: {},
|
|
||||||
readable: true,
|
|
||||||
}
|
|
||||||
return await config.api.query.previewQuery(queryPreview)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function previewPost(
|
async function previewPost(
|
||||||
datasource: Datasource,
|
datasource: Datasource,
|
||||||
fields: any,
|
fields: any,
|
||||||
params: QueryParameter[]
|
params: any
|
||||||
) {
|
) {
|
||||||
const queryPreview: QueryPreview = {
|
return config.previewQuery(
|
||||||
datasourceId: datasource._id!,
|
request,
|
||||||
parameters: params,
|
config,
|
||||||
|
datasource,
|
||||||
fields,
|
fields,
|
||||||
queryVerb: "create",
|
params,
|
||||||
name: datasource.name!,
|
"create"
|
||||||
transformer: null,
|
)
|
||||||
schema: {},
|
|
||||||
readable: false,
|
|
||||||
}
|
|
||||||
return await config.api.query.previewQuery(queryPreview)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should parse global and query level header mappings", async () => {
|
it("should parse global and query level header mappings", async () => {
|
||||||
|
@ -551,7 +400,7 @@ describe("/queries", () => {
|
||||||
emailHdr: "{{[user].[email]}}",
|
emailHdr: "{{[user].[email]}}",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
const responseBody = await previewGet(
|
const res = await previewGet(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
|
@ -561,17 +410,17 @@ describe("/queries", () => {
|
||||||
secondHdr: "1234",
|
secondHdr: "1234",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]
|
undefined
|
||||||
)
|
)
|
||||||
|
|
||||||
const parsedRequest = JSON.parse(responseBody.extra.raw)
|
const parsedRequest = JSON.parse(res.body.extra.raw)
|
||||||
expect(parsedRequest.opts.headers).toEqual({
|
expect(parsedRequest.opts.headers).toEqual({
|
||||||
test: "headerVal",
|
test: "headerVal",
|
||||||
emailHdr: userDetails.email,
|
emailHdr: userDetails.email,
|
||||||
queryHdr: userDetails.firstName,
|
queryHdr: userDetails.firstName,
|
||||||
secondHdr: "1234",
|
secondHdr: "1234",
|
||||||
})
|
})
|
||||||
expect(responseBody.rows[0].url).toEqual(
|
expect(res.body.rows[0].url).toEqual(
|
||||||
"http://www.google.com?email=" + userDetails.email.replace("@", "%40")
|
"http://www.google.com?email=" + userDetails.email.replace("@", "%40")
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -581,21 +430,21 @@ describe("/queries", () => {
|
||||||
|
|
||||||
const datasource = await config.restDatasource()
|
const datasource = await config.restDatasource()
|
||||||
|
|
||||||
const responseBody = await previewGet(
|
const res = await previewGet(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
queryString:
|
queryString:
|
||||||
"test={{myEmail}}&testName={{myName}}&testParam={{testParam}}",
|
"test={{myEmail}}&testName={{myName}}&testParam={{testParam}}",
|
||||||
},
|
},
|
||||||
[
|
{
|
||||||
{ name: "myEmail", default: "{{[user].[email]}}" },
|
myEmail: "{{[user].[email]}}",
|
||||||
{ name: "myName", default: "{{[user].[firstName]}}" },
|
myName: "{{[user].[firstName]}}",
|
||||||
{ name: "testParam", default: "1234" },
|
testParam: "1234",
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(responseBody.rows[0].url).toEqual(
|
expect(res.body.rows[0].url).toEqual(
|
||||||
"http://www.google.com?test=" +
|
"http://www.google.com?test=" +
|
||||||
userDetails.email.replace("@", "%40") +
|
userDetails.email.replace("@", "%40") +
|
||||||
"&testName=" +
|
"&testName=" +
|
||||||
|
@ -608,7 +457,7 @@ describe("/queries", () => {
|
||||||
const userDetails = config.getUserDetails()
|
const userDetails = config.getUserDetails()
|
||||||
const datasource = await config.restDatasource()
|
const datasource = await config.restDatasource()
|
||||||
|
|
||||||
const responseBody = await previewPost(
|
const res = await previewPost(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
|
@ -617,14 +466,16 @@ describe("/queries", () => {
|
||||||
"This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}",
|
"This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}",
|
||||||
bodyType: "text",
|
bodyType: "text",
|
||||||
},
|
},
|
||||||
[{ name: "testParam", default: "1234" }]
|
{
|
||||||
|
testParam: "1234",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const parsedRequest = JSON.parse(responseBody.extra.raw)
|
const parsedRequest = JSON.parse(res.body.extra.raw)
|
||||||
expect(parsedRequest.opts.body).toEqual(
|
expect(parsedRequest.opts.body).toEqual(
|
||||||
`This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234`
|
`This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234`
|
||||||
)
|
)
|
||||||
expect(responseBody.rows[0].url).toEqual(
|
expect(res.body.rows[0].url).toEqual(
|
||||||
"http://www.google.com?testParam=1234"
|
"http://www.google.com?testParam=1234"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -633,7 +484,7 @@ describe("/queries", () => {
|
||||||
const userDetails = config.getUserDetails()
|
const userDetails = config.getUserDetails()
|
||||||
const datasource = await config.restDatasource()
|
const datasource = await config.restDatasource()
|
||||||
|
|
||||||
const responseBody = await previewPost(
|
const res = await previewPost(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
|
@ -642,16 +493,16 @@ describe("/queries", () => {
|
||||||
'{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}',
|
'{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}',
|
||||||
bodyType: "json",
|
bodyType: "json",
|
||||||
},
|
},
|
||||||
[
|
{
|
||||||
{ name: "testParam", default: "1234" },
|
testParam: "1234",
|
||||||
{ name: "userRef", default: "{{[user].[firstName]}}" },
|
userRef: "{{[user].[firstName]}}",
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const parsedRequest = JSON.parse(responseBody.extra.raw)
|
const parsedRequest = JSON.parse(res.body.extra.raw)
|
||||||
const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}`
|
const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}`
|
||||||
expect(parsedRequest.opts.body).toEqual(test)
|
expect(parsedRequest.opts.body).toEqual(test)
|
||||||
expect(responseBody.rows[0].url).toEqual(
|
expect(res.body.rows[0].url).toEqual(
|
||||||
"http://www.google.com?testParam=1234"
|
"http://www.google.com?testParam=1234"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -660,7 +511,7 @@ describe("/queries", () => {
|
||||||
const userDetails = config.getUserDetails()
|
const userDetails = config.getUserDetails()
|
||||||
const datasource = await config.restDatasource()
|
const datasource = await config.restDatasource()
|
||||||
|
|
||||||
const responseBody = await previewPost(
|
const res = await previewPost(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
|
@ -670,17 +521,17 @@ describe("/queries", () => {
|
||||||
"<ref>{{userId}}</ref> <somestring>testing</somestring> </note>",
|
"<ref>{{userId}}</ref> <somestring>testing</somestring> </note>",
|
||||||
bodyType: "xml",
|
bodyType: "xml",
|
||||||
},
|
},
|
||||||
[
|
{
|
||||||
{ name: "testParam", default: "1234" },
|
testParam: "1234",
|
||||||
{ name: "userId", default: "{{[user].[firstName]}}" },
|
userId: "{{[user].[firstName]}}",
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const parsedRequest = JSON.parse(responseBody.extra.raw)
|
const parsedRequest = JSON.parse(res.body.extra.raw)
|
||||||
const test = `<note> <email>${userDetails.email}</email> <code>1234</code> <ref>${userDetails.firstName}</ref> <somestring>testing</somestring> </note>`
|
const test = `<note> <email>${userDetails.email}</email> <code>1234</code> <ref>${userDetails.firstName}</ref> <somestring>testing</somestring> </note>`
|
||||||
|
|
||||||
expect(parsedRequest.opts.body).toEqual(test)
|
expect(parsedRequest.opts.body).toEqual(test)
|
||||||
expect(responseBody.rows[0].url).toEqual(
|
expect(res.body.rows[0].url).toEqual(
|
||||||
"http://www.google.com?testParam=1234"
|
"http://www.google.com?testParam=1234"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -689,7 +540,7 @@ describe("/queries", () => {
|
||||||
const userDetails = config.getUserDetails()
|
const userDetails = config.getUserDetails()
|
||||||
const datasource = await config.restDatasource()
|
const datasource = await config.restDatasource()
|
||||||
|
|
||||||
const responseBody = await previewPost(
|
const res = await previewPost(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
|
@ -698,13 +549,13 @@ describe("/queries", () => {
|
||||||
'{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}',
|
'{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}',
|
||||||
bodyType: "form",
|
bodyType: "form",
|
||||||
},
|
},
|
||||||
[
|
{
|
||||||
{ name: "testParam", default: "1234" },
|
testParam: "1234",
|
||||||
{ name: "userRef", default: "{{[user].[firstName]}}" },
|
userRef: "{{[user].[firstName]}}",
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const parsedRequest = JSON.parse(responseBody.extra.raw)
|
const parsedRequest = JSON.parse(res.body.extra.raw)
|
||||||
|
|
||||||
const emailData = parsedRequest.opts.body._streams[1]
|
const emailData = parsedRequest.opts.body._streams[1]
|
||||||
expect(emailData).toEqual(userDetails.email)
|
expect(emailData).toEqual(userDetails.email)
|
||||||
|
@ -715,7 +566,7 @@ describe("/queries", () => {
|
||||||
const userRef = parsedRequest.opts.body._streams[7]
|
const userRef = parsedRequest.opts.body._streams[7]
|
||||||
expect(userRef).toEqual(userDetails.firstName)
|
expect(userRef).toEqual(userDetails.firstName)
|
||||||
|
|
||||||
expect(responseBody.rows[0].url).toEqual(
|
expect(res.body.rows[0].url).toEqual(
|
||||||
"http://www.google.com?testParam=1234"
|
"http://www.google.com?testParam=1234"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -724,7 +575,7 @@ describe("/queries", () => {
|
||||||
const userDetails = config.getUserDetails()
|
const userDetails = config.getUserDetails()
|
||||||
const datasource = await config.restDatasource()
|
const datasource = await config.restDatasource()
|
||||||
|
|
||||||
const responseBody = await previewPost(
|
const res = await previewPost(
|
||||||
datasource,
|
datasource,
|
||||||
{
|
{
|
||||||
path: "www.google.com",
|
path: "www.google.com",
|
||||||
|
@ -733,12 +584,12 @@ describe("/queries", () => {
|
||||||
'{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}',
|
'{"email":"{{[user].[email]}}","queryCode":{{testParam}},"userRef":"{{userRef}}"}',
|
||||||
bodyType: "encoded",
|
bodyType: "encoded",
|
||||||
},
|
},
|
||||||
[
|
{
|
||||||
{ name: "testParam", default: "1234" },
|
testParam: "1234",
|
||||||
{ name: "userRef", default: "{{[user].[firstName]}}" },
|
userRef: "{{[user].[firstName]}}",
|
||||||
]
|
}
|
||||||
)
|
)
|
||||||
const parsedRequest = JSON.parse(responseBody.extra.raw)
|
const parsedRequest = JSON.parse(res.body.extra.raw)
|
||||||
|
|
||||||
expect(parsedRequest.opts.body.email).toEqual(userDetails.email)
|
expect(parsedRequest.opts.body.email).toEqual(userDetails.email)
|
||||||
expect(parsedRequest.opts.body.queryCode).toEqual("1234")
|
expect(parsedRequest.opts.body.queryCode).toEqual("1234")
|
||||||
|
|
|
@ -866,6 +866,28 @@ export default class TestConfiguration {
|
||||||
|
|
||||||
// QUERY
|
// QUERY
|
||||||
|
|
||||||
|
async previewQuery(
|
||||||
|
request: any,
|
||||||
|
config: any,
|
||||||
|
datasource: any,
|
||||||
|
fields: any,
|
||||||
|
params: any,
|
||||||
|
verb?: string
|
||||||
|
) {
|
||||||
|
return request
|
||||||
|
.post(`/api/queries/preview`)
|
||||||
|
.send({
|
||||||
|
datasourceId: datasource._id,
|
||||||
|
parameters: params || {},
|
||||||
|
fields,
|
||||||
|
queryVerb: verb || "read",
|
||||||
|
name: datasource.name,
|
||||||
|
})
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
}
|
||||||
|
|
||||||
async createQuery(config?: any) {
|
async createQuery(config?: any) {
|
||||||
if (!this.datasource && !config) {
|
if (!this.datasource && !config) {
|
||||||
throw "No datasource created for query."
|
throw "No datasource created for query."
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import TestConfiguration from "../TestConfiguration"
|
import TestConfiguration from "../TestConfiguration"
|
||||||
import {
|
import {
|
||||||
Query,
|
Query,
|
||||||
QueryPreview,
|
|
||||||
type ExecuteQueryRequest,
|
type ExecuteQueryRequest,
|
||||||
type ExecuteQueryResponse,
|
type ExecuteQueryResponse,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
@ -42,19 +41,4 @@ export class QueryAPI extends TestAPI {
|
||||||
|
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
previewQuery = async (queryPreview: QueryPreview) => {
|
|
||||||
const res = await this.request
|
|
||||||
.post(`/api/queries/preview`)
|
|
||||||
.send(queryPreview)
|
|
||||||
.set(this.config.defaultHeaders())
|
|
||||||
.expect("Content-Type", /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
if (res.status !== 200) {
|
|
||||||
throw new Error(JSON.stringify(res.body))
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.body
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,7 +366,7 @@ export function basicDatasource(): { datasource: Datasource } {
|
||||||
|
|
||||||
export function basicQuery(datasourceId: string): Query {
|
export function basicQuery(datasourceId: string): Query {
|
||||||
return {
|
return {
|
||||||
datasourceId,
|
datasourceId: datasourceId,
|
||||||
name: "New Query",
|
name: "New Query",
|
||||||
parameters: [],
|
parameters: [],
|
||||||
fields: {},
|
fields: {},
|
||||||
|
|
|
@ -7,10 +7,10 @@ export interface QueryEvent {
|
||||||
datasource: Datasource
|
datasource: Datasource
|
||||||
queryVerb: string
|
queryVerb: string
|
||||||
fields: { [key: string]: any }
|
fields: { [key: string]: any }
|
||||||
parameters: { [key: string]: unknown }
|
parameters: { [key: string]: any }
|
||||||
pagination?: any
|
pagination?: any
|
||||||
transformer: any
|
transformer: any
|
||||||
queryId?: string
|
queryId: string
|
||||||
environmentVariables?: Record<string, string>
|
environmentVariables?: Record<string, string>
|
||||||
ctx?: any
|
ctx?: any
|
||||||
schema?: Record<string, QuerySchema | string>
|
schema?: Record<string, QuerySchema | string>
|
||||||
|
|
|
@ -43,7 +43,7 @@ class QueryRunner {
|
||||||
this.parameters = input.parameters
|
this.parameters = input.parameters
|
||||||
this.pagination = input.pagination
|
this.pagination = input.pagination
|
||||||
this.transformer = input.transformer
|
this.transformer = input.transformer
|
||||||
this.queryId = input.queryId!
|
this.queryId = input.queryId
|
||||||
this.schema = input.schema
|
this.schema = input.schema
|
||||||
this.noRecursiveQuery = flags.noRecursiveQuery
|
this.noRecursiveQuery = flags.noRecursiveQuery
|
||||||
this.cachedVariables = []
|
this.cachedVariables = []
|
||||||
|
|
|
@ -19,7 +19,7 @@ export interface Query extends Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryPreview extends Omit<Query, "_id"> {
|
export interface QueryPreview extends Omit<Query, "_id"> {
|
||||||
queryId?: string
|
queryId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueryParameter {
|
export interface QueryParameter {
|
||||||
|
|
Loading…
Reference in New Issue