@budibase/server 2.4.11 → 2.4.12-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,6 +11,8 @@ import {
11
11
  Row,
12
12
  Table,
13
13
  RelationshipTypes,
14
+ FieldType,
15
+ SortType,
14
16
  } from "@budibase/types"
15
17
  import {
16
18
  breakRowIdField,
@@ -749,8 +751,16 @@ export class ExternalRequest {
749
751
  )
750
752
  //if the sort column is a formula, remove it
751
753
  for (let sortColumn of Object.keys(sort || {})) {
752
- if (table.schema[sortColumn]?.type === "formula") {
753
- delete sort?.[sortColumn]
754
+ if (!sort?.[sortColumn]) {
755
+ continue
756
+ }
757
+ switch (table.schema[sortColumn]?.type) {
758
+ case FieldType.FORMULA:
759
+ delete sort?.[sortColumn]
760
+ break
761
+ case FieldType.NUMBER:
762
+ sort[sortColumn].type = SortType.number
763
+ break
754
764
  }
755
765
  }
756
766
  filters = buildFilters(id, filters || {}, table)
@@ -8,7 +8,6 @@ import {
8
8
  breakRowIdField,
9
9
  } from "../../../integrations/utils"
10
10
  import { ExternalRequest, RunConfig } from "./ExternalRequest"
11
- import { context } from "@budibase/backend-core"
12
11
  import * as exporters from "../view/exporters"
13
12
  import { apiFileReturn } from "../../../utilities/fileSystem"
14
13
  import {
@@ -19,6 +18,7 @@ import {
19
18
  Table,
20
19
  Datasource,
21
20
  IncludeRelationship,
21
+ SortJson,
22
22
  } from "@budibase/types"
23
23
  import sdk from "../../../sdk"
24
24
 
@@ -142,14 +142,14 @@ export async function search(ctx: BBContext) {
142
142
  limit: limit,
143
143
  }
144
144
  }
145
- let sort
145
+ let sort: SortJson | undefined
146
146
  if (params.sort) {
147
147
  const direction =
148
148
  params.sortOrder === "descending"
149
149
  ? SortDirection.DESCENDING
150
150
  : SortDirection.ASCENDING
151
151
  sort = {
152
- [params.sort]: direction,
152
+ [params.sort]: { direction },
153
153
  }
154
154
  }
155
155
  try {
@@ -1,4 +1,5 @@
1
1
  import appEndpoints from "./applications"
2
+ import metricEndpoints from "./metrics"
2
3
  import queryEndpoints from "./queries"
3
4
  import tableEndpoints from "./tables"
4
5
  import rowEndpoints from "./rows"
@@ -12,7 +13,7 @@ import env from "../../../environment"
12
13
  // below imports don't have declaration files
13
14
  const Router = require("@koa/router")
14
15
  const { RateLimit, Stores } = require("koa2-ratelimit")
15
- import { redis, permissions } from "@budibase/backend-core"
16
+ import { middleware, redis, permissions } from "@budibase/backend-core"
16
17
  const { PermissionType, PermissionLevel } = permissions
17
18
 
18
19
  const PREFIX = "/api/public/v1"
@@ -91,6 +92,13 @@ function addToRouter(endpoints: any) {
91
92
  }
92
93
  }
93
94
 
95
+ function applyAdminRoutes(endpoints: any) {
96
+ addMiddleware(endpoints.read, middleware.builderOrAdmin)
97
+ addMiddleware(endpoints.write, middleware.builderOrAdmin)
98
+ addToRouter(endpoints.read)
99
+ addToRouter(endpoints.write)
100
+ }
101
+
94
102
  function applyRoutes(
95
103
  endpoints: any,
96
104
  permType: string,
@@ -119,6 +127,7 @@ function applyRoutes(
119
127
  addToRouter(endpoints.write)
120
128
  }
121
129
 
130
+ applyAdminRoutes(metricEndpoints)
122
131
  applyRoutes(appEndpoints, PermissionType.APP, "appId")
123
132
  applyRoutes(tableEndpoints, PermissionType.TABLE, "tableId")
124
133
  applyRoutes(userEndpoints, PermissionType.USER, "userId")
@@ -0,0 +1,28 @@
1
+ import controller from "../../controllers/public/metrics"
2
+ import Endpoint from "./utils/Endpoint"
3
+
4
+ const read = []
5
+
6
+ /**
7
+ * @openapi
8
+ * /metrics:
9
+ * get:
10
+ * operationId: metricsGet
11
+ * summary: Retrieve Budibase tenant metrics
12
+ * description: Output metrics in OpenMetrics format compatible with Prometheus
13
+ * tags:
14
+ * - metrics
15
+ * responses:
16
+ * 200:
17
+ * description: Returns tenant metrics.
18
+ * content:
19
+ * text/plain:
20
+ * schema:
21
+ * type: string
22
+ * examples:
23
+ * metrics:
24
+ * $ref: '#/components/examples/metrics'
25
+ */
26
+ read.push(new Endpoint("get", "/metrics", controller.fetch))
27
+
28
+ export default { read }
@@ -0,0 +1,34 @@
1
+ const setup = require("../../tests/utilities")
2
+
3
+ jest.setTimeout(30000)
4
+
5
+ describe("/metrics", () => {
6
+ let request = setup.getRequest()
7
+ let config = setup.getConfig()
8
+
9
+ afterAll(setup.afterAll)
10
+
11
+ // For some reason this cannot be a beforeAll or the test "should be able to update the user" fail
12
+ beforeEach(async () => {
13
+ await config.init()
14
+ })
15
+
16
+ describe("get", () => {
17
+ it("returns a list of metrics", async () => {
18
+ const res = await request
19
+ .get(`/api/public/v1/metrics`)
20
+ .set(config.defaultHeaders())
21
+ .expect("Content-Type", /text\/plain/)
22
+ .expect(200)
23
+ expect(res.text).toContain("budibase_tenant_user_count")
24
+ })
25
+
26
+ it("endpoint should not be publicly exposed", async () => {
27
+ await request
28
+ .get(`/api/public/v1/metrics`)
29
+ .set(config.publicHeaders())
30
+ .expect(403)
31
+ })
32
+ })
33
+
34
+ })
@@ -22,6 +22,10 @@ export interface paths {
22
22
  /** Based on application properties (currently only name) search for applications. */
23
23
  post: operations["appSearch"];
24
24
  };
25
+ "/metrics": {
26
+ /** Output metrics in OpenMetrics format compatible with Prometheus */
27
+ get: operations["metricsGet"];
28
+ };
25
29
  "/queries/{queryId}": {
26
30
  /** Queries which have been created within a Budibase app can be executed using this, */
27
31
  post: operations["queryExecute"];
@@ -844,6 +848,17 @@ export interface operations {
844
848
  };
845
849
  };
846
850
  };
851
+ /** Output metrics in OpenMetrics format compatible with Prometheus */
852
+ metricsGet: {
853
+ responses: {
854
+ /** Returns tenant metrics. */
855
+ 200: {
856
+ content: {
857
+ "text/plain": string;
858
+ };
859
+ };
860
+ };
861
+ };
847
862
  /** Queries which have been created within a Budibase app can be executed using this, */
848
863
  queryExecute: {
849
864
  parameters: {
@@ -317,7 +317,8 @@ class InternalBuilder {
317
317
  const table = json.meta?.table
318
318
  if (sort) {
319
319
  for (let [key, value] of Object.entries(sort)) {
320
- const direction = value === SortDirection.ASCENDING ? "asc" : "desc"
320
+ const direction =
321
+ value.direction === SortDirection.ASCENDING ? "asc" : "desc"
321
322
  query = query.orderBy(`${table?.name}.${key}`, direction)
322
323
  }
323
324
  } else if (this.client === SqlClient.MS_SQL && paginate?.limit) {
@@ -2,8 +2,11 @@ import {
2
2
  DatasourceFieldType,
3
3
  DatasourcePlus,
4
4
  Integration,
5
+ PaginationJson,
5
6
  QueryJson,
6
7
  QueryType,
8
+ SearchFilters,
9
+ SortJson,
7
10
  Table,
8
11
  TableSchema,
9
12
  } from "@budibase/types"
@@ -13,6 +16,7 @@ import { DataSourceOperation, FieldTypes } from "../constants"
13
16
  import { GoogleSpreadsheet } from "google-spreadsheet"
14
17
  import fetch from "node-fetch"
15
18
  import { configs, HTTPError } from "@budibase/backend-core"
19
+ import { dataFilters } from "@budibase/shared-core"
16
20
 
17
21
  interface GoogleSheetsConfig {
18
22
  spreadsheetId: string
@@ -237,7 +241,7 @@ class GoogleSheetsIntegration implements DatasourcePlus {
237
241
  const handlers = {
238
242
  [DataSourceOperation.CREATE]: () =>
239
243
  this.create({ sheet, row: json.body }),
240
- [DataSourceOperation.READ]: () => this.read({ sheet }),
244
+ [DataSourceOperation.READ]: () => this.read({ ...json, sheet }),
241
245
  [DataSourceOperation.UPDATE]: () =>
242
246
  this.update({
243
247
  // exclude the header row and zero index
@@ -345,18 +349,40 @@ class GoogleSheetsIntegration implements DatasourcePlus {
345
349
  }
346
350
  }
347
351
 
348
- async read(query: { sheet: string }) {
352
+ async read(query: {
353
+ sheet: string
354
+ filters?: SearchFilters
355
+ sort?: SortJson
356
+ paginate?: PaginationJson
357
+ }) {
349
358
  try {
350
359
  await this.connect()
351
360
  const sheet = this.client.sheetsByTitle[query.sheet]
352
361
  const rows = await sheet.getRows()
362
+ const filtered = dataFilters.runLuceneQuery(rows, query.filters)
353
363
  const headerValues = sheet.headerValues
354
- const response = []
355
- for (let row of rows) {
364
+ let response = []
365
+ for (let row of filtered) {
356
366
  response.push(
357
367
  this.buildRowObject(headerValues, row._rawData, row._rowNumber)
358
368
  )
359
369
  }
370
+
371
+ if (query.sort) {
372
+ if (Object.keys(query.sort).length !== 1) {
373
+ console.warn("Googlesheets does not support multiple sorting", {
374
+ sortInfo: query.sort,
375
+ })
376
+ }
377
+ const [sortField, sortInfo] = Object.entries(query.sort)[0]
378
+ response = dataFilters.luceneSort(
379
+ response,
380
+ sortField,
381
+ sortInfo.direction,
382
+ sortInfo.type
383
+ )
384
+ }
385
+
360
386
  return response
361
387
  } catch (err) {
362
388
  console.error("Error reading from google sheets", err)
package/tsconfig.json CHANGED
@@ -9,6 +9,7 @@
9
9
  "@budibase/types": ["../types/src"],
10
10
  "@budibase/backend-core": ["../backend-core/src"],
11
11
  "@budibase/backend-core/*": ["../backend-core/*"],
12
+ "@budibase/shared-core": ["../shared-core/src"],
12
13
  "@budibase/pro": ["../../../budibase-pro/packages/pro/src"]
13
14
  }
14
15
  },
@@ -19,15 +20,9 @@
19
20
  "references": [
20
21
  { "path": "../types" },
21
22
  { "path": "../backend-core" },
23
+ { "path": "../shared-core" },
22
24
  { "path": "../../../budibase-pro/packages/pro" }
23
25
  ],
24
- "include": [
25
- "src/**/*",
26
- "specs",
27
- "package.json"
28
- ],
29
- "exclude": [
30
- "node_modules",
31
- "dist"
32
- ]
33
- }
26
+ "include": ["src/**/*", "specs", "package.json"],
27
+ "exclude": ["node_modules", "dist"]
28
+ }