Final update to view mapping and typing.
This commit is contained in:
parent
82c7c089cb
commit
cdf7cb9fab
|
@ -47,12 +47,10 @@ export async function search(ctx: UserCtx, next: Next) {
|
|||
|
||||
export async function viewSearch(ctx: UserCtx, next: Next) {
|
||||
ctx.request.body = getSearchParameters(ctx)
|
||||
await rowController.views.searchView({
|
||||
...ctx,
|
||||
params: {
|
||||
viewId: ctx.params.viewId,
|
||||
},
|
||||
})
|
||||
ctx.params = {
|
||||
viewId: ctx.params.viewId,
|
||||
}
|
||||
await rowController.views.searchView(ctx)
|
||||
await next()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,72 +1,88 @@
|
|||
import { search as stringSearch } from "./utils"
|
||||
import * as controller from "../view"
|
||||
import { ViewV2, UserCtx } from "@budibase/types"
|
||||
import { ViewV2, UserCtx, UISearchFilter, PublicAPIView } from "@budibase/types"
|
||||
import { Next } from "koa"
|
||||
import { merge } from "lodash"
|
||||
|
||||
function fixView(view: ViewV2, params?: { viewId: string }) {
|
||||
if (!params || !view) {
|
||||
return view
|
||||
function viewRequest(view: PublicAPIView, params?: { viewId: string }) {
|
||||
const viewV2: ViewV2 = view
|
||||
if (!viewV2) {
|
||||
return viewV2
|
||||
}
|
||||
if (params?.viewId) {
|
||||
view.id = params.viewId
|
||||
viewV2.id = params.viewId
|
||||
}
|
||||
if (!view.query) {
|
||||
view.query = {}
|
||||
viewV2.query = {}
|
||||
} else {
|
||||
// public API only has one form of query
|
||||
viewV2.queryUI = viewV2.query as UISearchFilter
|
||||
}
|
||||
view.version = 2
|
||||
return view
|
||||
viewV2.version = 2
|
||||
return viewV2
|
||||
}
|
||||
|
||||
function viewResponse(view: ViewV2): PublicAPIView {
|
||||
// remove our internal structure - always un-necessary
|
||||
delete view.query
|
||||
return {
|
||||
...view,
|
||||
query: view.queryUI,
|
||||
}
|
||||
}
|
||||
|
||||
function viewsResponse(views: ViewV2[]): PublicAPIView[] {
|
||||
return views.map(viewResponse)
|
||||
}
|
||||
|
||||
export async function search(ctx: UserCtx, next: Next) {
|
||||
const { name } = ctx.request.body
|
||||
await controller.v2.fetch(ctx)
|
||||
ctx.body = stringSearch(ctx.body.data, name)
|
||||
ctx.body.data = viewsResponse(stringSearch(ctx.body.data, name))
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function create(ctx: UserCtx, next: Next) {
|
||||
await controller.v2.create(
|
||||
merge(ctx, {
|
||||
request: {
|
||||
body: fixView(ctx.request.body),
|
||||
},
|
||||
})
|
||||
)
|
||||
ctx = merge(ctx, {
|
||||
request: {
|
||||
body: viewRequest(ctx.request.body),
|
||||
},
|
||||
})
|
||||
await controller.v2.create(ctx)
|
||||
ctx.body.data = viewResponse(ctx.body.data)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function read(ctx: UserCtx, next: Next) {
|
||||
await controller.v2.get(
|
||||
merge(ctx, {
|
||||
params: {
|
||||
viewId: ctx.params.viewId,
|
||||
},
|
||||
})
|
||||
)
|
||||
ctx = merge(ctx, {
|
||||
params: {
|
||||
viewId: ctx.params.viewId,
|
||||
},
|
||||
})
|
||||
await controller.v2.get(ctx)
|
||||
ctx.body.data = viewResponse(ctx.body.data)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function update(ctx: UserCtx, next: Next) {
|
||||
const viewId = ctx.params.viewId
|
||||
await controller.v2.update(
|
||||
merge(ctx, {
|
||||
request: {
|
||||
body: {
|
||||
data: fixView(ctx.request.body, { viewId }),
|
||||
},
|
||||
ctx = merge(ctx, {
|
||||
request: {
|
||||
body: {
|
||||
data: viewRequest(ctx.request.body, { viewId }),
|
||||
},
|
||||
params: {
|
||||
viewId,
|
||||
},
|
||||
})
|
||||
)
|
||||
},
|
||||
params: {
|
||||
viewId,
|
||||
},
|
||||
})
|
||||
await controller.v2.update(ctx)
|
||||
ctx.body.data = viewResponse(ctx.body.data)
|
||||
await next()
|
||||
}
|
||||
|
||||
export async function destroy(ctx: UserCtx, next: Next) {
|
||||
await controller.v2.remove(ctx)
|
||||
ctx.body = ctx.table
|
||||
await next()
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ export async function searchView(
|
|||
result.rows.forEach(r => (r._viewId = view.id))
|
||||
ctx.body = result
|
||||
}
|
||||
|
||||
function getSortOptions(request: SearchViewRowRequest, view: ViewV2) {
|
||||
if (request.sort) {
|
||||
return {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import controller from "../../controllers/public/rows"
|
||||
import controller, { viewSearch } from "../../controllers/public/rows"
|
||||
import Endpoint from "./utils/Endpoint"
|
||||
import { externalSearchValidator } from "../utils/validators"
|
||||
|
||||
|
@ -200,7 +200,7 @@ read.push(
|
|||
new Endpoint(
|
||||
"post",
|
||||
"/views/:viewId/rows/search",
|
||||
controller.search
|
||||
controller.viewSearch
|
||||
).addMiddleware(externalSearchValidator())
|
||||
)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
ViewV2Schema,
|
||||
ViewV2,
|
||||
ViewV2Type,
|
||||
PublicAPIView,
|
||||
} from "@budibase/types"
|
||||
import { HttpMethod, MakeRequestResponse, generateMakeRequest } from "./utils"
|
||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||
|
@ -148,17 +149,17 @@ export class PublicViewAPI {
|
|||
}
|
||||
|
||||
async create(
|
||||
view: Omit<ViewV2, "id" | "version">,
|
||||
view: Omit<PublicAPIView, "id" | "version">,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<ViewV2>> {
|
||||
): Promise<Response<PublicAPIView>> {
|
||||
return this.request.send("post", "/views", view, expectations)
|
||||
}
|
||||
|
||||
async update(
|
||||
viewId: string,
|
||||
view: Omit<ViewV2, "id" | "version">,
|
||||
view: Omit<PublicAPIView, "id" | "version">,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<ViewV2>> {
|
||||
): Promise<Response<PublicAPIView>> {
|
||||
return this.request.send("put", `/views/${viewId}`, view, expectations)
|
||||
}
|
||||
|
||||
|
@ -177,20 +178,14 @@ export class PublicViewAPI {
|
|||
async find(
|
||||
viewId: string,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<ViewV2>> {
|
||||
): Promise<Response<PublicAPIView>> {
|
||||
return this.request.send("get", `/views/${viewId}`, undefined, expectations)
|
||||
}
|
||||
|
||||
async fetch(
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<ViewV2[]>> {
|
||||
return this.request.send("get", "/views", undefined, expectations)
|
||||
}
|
||||
|
||||
async search(
|
||||
viewName: string,
|
||||
expectations?: PublicAPIExpectations
|
||||
): Promise<Response<ViewV2[]>> {
|
||||
): Promise<Response<PublicAPIView[]>> {
|
||||
return this.request.send(
|
||||
"post",
|
||||
"/views/search",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import * as setup from "../../tests/utilities"
|
||||
import { basicTable } from "../../../../tests/utilities/structures"
|
||||
import { Table } from "@budibase/types"
|
||||
import { BasicOperator, Table, UILogicalOperator } from "@budibase/types"
|
||||
import { PublicAPIRequest } from "./Request"
|
||||
import { generator } from "@budibase/backend-core/tests"
|
||||
|
||||
|
@ -34,14 +34,10 @@ describe("check public API security", () => {
|
|||
|
||||
it("should be able to update a view", async () => {
|
||||
const view = await request.views.create(baseView(), { status: 201 })
|
||||
await request.views.update(
|
||||
view.data.id,
|
||||
{
|
||||
...view.data,
|
||||
name: "new name",
|
||||
},
|
||||
{ status: 200 }
|
||||
)
|
||||
const response = await request.views.update(view.data.id, {
|
||||
...view.data,
|
||||
name: "new name",
|
||||
})
|
||||
})
|
||||
|
||||
it("should be able to search views", async () => {
|
||||
|
@ -77,9 +73,18 @@ describe("check public API security", () => {
|
|||
{
|
||||
...baseView(),
|
||||
query: {
|
||||
string: {
|
||||
name: "hello",
|
||||
},
|
||||
logicalOperator: UILogicalOperator.ANY,
|
||||
groups: [
|
||||
{
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.STRING,
|
||||
field: "name",
|
||||
value: "hello",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ status: 201 }
|
||||
|
|
|
@ -12,6 +12,10 @@ import {
|
|||
ViewV2Type,
|
||||
SortOrder,
|
||||
SortType,
|
||||
UILogicalOperator,
|
||||
BasicOperator,
|
||||
ArrayOperator,
|
||||
RangeOperator,
|
||||
} from "@budibase/types"
|
||||
import Joi, { CustomValidator } from "joi"
|
||||
import { ValidSnippetNameRegex, helpers } from "@budibase/shared-core"
|
||||
|
@ -69,6 +73,43 @@ export function tableValidator() {
|
|||
)
|
||||
}
|
||||
|
||||
function searchUIFilterValidator() {
|
||||
const logicalOperator = Joi.string().valid(
|
||||
...Object.values(UILogicalOperator)
|
||||
)
|
||||
const operators = [
|
||||
...Object.values(BasicOperator),
|
||||
...Object.values(ArrayOperator),
|
||||
...Object.values(RangeOperator),
|
||||
]
|
||||
const filters = Joi.array().items(
|
||||
Joi.object({
|
||||
operator: Joi.string()
|
||||
.valid(...operators)
|
||||
.required(),
|
||||
field: Joi.string().required(),
|
||||
// could do with better validation of value based on operator
|
||||
value: Joi.any().required(),
|
||||
})
|
||||
)
|
||||
return Joi.object({
|
||||
logicalOperator,
|
||||
onEmptyFilter: Joi.string().valid(...Object.values(EmptyFilterOption)),
|
||||
groups: Joi.array().items(
|
||||
Joi.object({
|
||||
logicalOperator,
|
||||
filters,
|
||||
groups: Joi.array().items(
|
||||
Joi.object({
|
||||
filters,
|
||||
logicalOperator,
|
||||
})
|
||||
),
|
||||
})
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
export function viewValidator() {
|
||||
return auth.joiValidator.body(
|
||||
Joi.object({
|
||||
|
@ -78,7 +119,7 @@ export function viewValidator() {
|
|||
type: Joi.string().optional().valid(null, ViewV2Type.CALCULATION),
|
||||
primaryDisplay: OPTIONAL_STRING,
|
||||
schema: Joi.object().required(),
|
||||
query: searchFiltersValidator().optional(),
|
||||
query: searchUIFilterValidator().optional(),
|
||||
sort: Joi.object({
|
||||
field: Joi.string().required(),
|
||||
order: Joi.string()
|
||||
|
|
Loading…
Reference in New Issue