diff --git a/packages/bbui/package.json b/packages/bbui/package.json index d88bb8a3d8..cea1075963 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -38,6 +38,7 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "1.2.1", + "@spectrum-css/accordion": "3.0.24", "@budibase/string-templates": "2.2.12-alpha.33", "@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actiongroup": "1.0.1", @@ -89,4 +90,4 @@ "loader-utils": "1.4.1" }, "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc" -} +} \ No newline at end of file diff --git a/packages/bbui/src/Accordion/Accordion.svelte b/packages/bbui/src/Accordion/Accordion.svelte new file mode 100644 index 0000000000..1c88450c9a --- /dev/null +++ b/packages/bbui/src/Accordion/Accordion.svelte @@ -0,0 +1,58 @@ + + +
+
+

+ + +

+
+ +
+
+
+ + diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index 3de0bc2f46..b56aa597ad 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -75,6 +75,7 @@ export { default as ListItem } from "./List/ListItem.svelte" export { default as IconSideNav } from "./IconSideNav/IconSideNav.svelte" export { default as IconSideNavItem } from "./IconSideNav/IconSideNavItem.svelte" export { default as Slider } from "./Form/Slider.svelte" +export { default as Accordion } from "./Accordion/Accordion.svelte" // Renderers export { default as BoldRenderer } from "./Table/BoldRenderer.svelte" diff --git a/packages/bbui/yarn.lock b/packages/bbui/yarn.lock index 72e36a6474..3286c33e69 100644 --- a/packages/bbui/yarn.lock +++ b/packages/bbui/yarn.lock @@ -109,10 +109,15 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@spectrum-css/actionbutton@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.1.tgz#9c75da37ea6915919fb574c74bd60dacc03b6577" - integrity sha512-AUqtyNabHF451Aj9i3xz82TxS5Z6k1dttA68/1hMeU9kbPCSS4P6Viw3vaRGs9CSspuR8xnnhDgrq+F+zMy2Hw== +"@spectrum-css/accordion@3.0.24": + version "3.0.24" + resolved "https://registry.yarnpkg.com/@spectrum-css/accordion/-/accordion-3.0.24.tgz#f89066c120c57b0cfc9aba66d60c39fc1cf69f74" + integrity sha512-jNOmUsxmiT3lRLButnN5KKHM94fd+87fjiF8L0c4uRNgJl6ZsBuxPXrM15lV4y1f8D2IACAw01/ZkGRAeaCOFA== + +"@spectrum-css/actionbutton@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@spectrum-css/actionbutton/-/actionbutton-1.0.2.tgz#7753a94c64cebecfca6749ef20e37a5ea80c59be" + integrity sha512-laDWk7PCgy2I0AGsMjTmYKkiMVYVoF1B4tffJf4cIp66znTiqPHEbLDh5EDNU88JLTY2bWoCOY4cJxvXk5gERw== "@spectrum-css/actiongroup@1.0.1": version "1.0.1" diff --git a/packages/builder/package.json b/packages/builder/package.json index a333827bbd..38f18cea0e 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -78,6 +78,7 @@ "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", + "@spectrum-css/accordion": "^3.0.24", "codemirror": "^5.59.0", "dayjs": "^1.11.2", "downloadjs": "1.4.7", @@ -123,4 +124,4 @@ "vite": "^3.0.8" }, "gitHead": "115189f72a850bfb52b65ec61d932531bf327072" -} +} \ No newline at end of file diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte index 05649e1773..6b35f3313f 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte @@ -6,6 +6,7 @@ Toggle, Button, TextArea, + Accordion, } from "@budibase/bbui" import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" import { capitalise } from "helpers" @@ -51,15 +52,24 @@ let addButton - function getDisplayName(key) { + function getDisplayName(key, fieldKey) { let name - if (schema[key]?.display) { + if (fieldKey && schema[key]["fields"][fieldKey]?.display) { + name = schema[key]["fields"][fieldKey].display + } else if (fieldKey) { + name = fieldKey + } else if (schema[key]?.display) { name = schema[key].display } else { name = key } return capitalise(name) } + function getFieldGroupKeys(fieldGroup) { + return Object.entries(schema[fieldGroup].fields || {}) + .filter(el => filter(el)) + .map(([key]) => key) + }
@@ -100,6 +110,27 @@ error={$validation.errors[configKey]} /> + {:else if schema[configKey].type === "fieldGroup"} + !!config[fieldKey] + )} + header={getDisplayName(configKey)} + > + + {#each getFieldGroupKeys(configKey) as fieldKey} +
+ + +
+ {/each} +
+
{:else}
diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index bf3f7defb7..a491d14404 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -1356,6 +1356,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@spectrum-css/accordion@^3.0.24": + version "3.0.24" + resolved "https://registry.yarnpkg.com/@spectrum-css/accordion/-/accordion-3.0.24.tgz#f89066c120c57b0cfc9aba66d60c39fc1cf69f74" + integrity sha512-jNOmUsxmiT3lRLButnN5KKHM94fd+87fjiF8L0c4uRNgJl6ZsBuxPXrM15lV4y1f8D2IACAw01/ZkGRAeaCOFA== + "@spectrum-css/page@^3.0.1": version "3.0.8" resolved "https://registry.yarnpkg.com/@spectrum-css/page/-/page-3.0.8.tgz#001efa9e4c10095df9b2b37cf7d7d6eb60140190" diff --git a/packages/server/src/integrations/mongodb.ts b/packages/server/src/integrations/mongodb.ts index 9ccefbda58..38b3891fe4 100644 --- a/packages/server/src/integrations/mongodb.ts +++ b/packages/server/src/integrations/mongodb.ts @@ -12,11 +12,16 @@ import { FindOneAndUpdateOptions, UpdateOptions, OperationOptions, + MongoClientOptions, } from "mongodb" +import environment from "../environment" interface MongoDBConfig { connectionString: string db: string + tlsCertificateFile: string + tlsCertificateKeyFile: string + tlsCAFile: string } interface MongoDBQuery { @@ -26,292 +31,331 @@ interface MongoDBQuery { } } -const SCHEMA: Integration = { - docs: "https://github.com/mongodb/node-mongodb-native", - friendlyName: "MongoDB", - type: "Non-relational", - description: - "MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era.", - datasource: { - connectionString: { - type: DatasourceFieldType.STRING, - required: true, - default: "mongodb://localhost:27017", - }, - db: { - type: DatasourceFieldType.STRING, - required: true, - }, - }, - query: { - create: { - type: QueryType.JSON, - }, - read: { - type: QueryType.JSON, - }, - update: { - type: QueryType.JSON, - }, - delete: { - type: QueryType.JSON, - }, - aggregate: { - type: QueryType.JSON, - readable: true, - steps: [ - { - key: "$addFields", - template: "{\n\t\n}", - }, - { - key: "$bucket", - template: `{ - "groupBy": "", - "boundaries": [], - "default": "", - "output": {} -}`, - }, - { - key: "$bucketAuto", - template: `{ - "groupBy": "", - "buckets": 1, - "output": {}, - "granularity": "R5" -}`, - }, - { - key: "$changeStream", - template: `{ - "allChangesForCluster": true, - "fullDocument": "", - "fullDocumentBeforeChange": "", - "resumeAfter": 1, - "showExpandedEvents": true, - "startAfter": {}, - "startAtOperationTime": "" -}`, - }, - { - key: "$collStats", - template: `{ - "latencyStats": { "histograms": true } }, - "storageStats": { "scale": 1 } }, - "count": {}, - "queryExecStats": {} -}`, - }, - { - key: "$count", - template: ``, - }, - { - key: "$densify", - template: `{ - "field": "", - "partitionByFields": [], - "range": { - "step": 1, - "unit": 1, - "bounds": "full" - } -}`, - }, - { - key: "$documents", - template: `[]`, - }, - { - key: "$facet", - template: `{\n\t\n}`, - }, - { - key: "$fill", - template: `{ - "partitionBy": "", - "partitionByFields": [], - "sortBy": {}, - "output": {} -}`, - }, - { - key: "$geoNear", - template: `{ - "near": { - "type": "Point", - "coordinates": [ - -73.98142, 40.71782 - ] - }, - "key": "location", - "distanceField": "dist.calculated", - "query": { "category": "Parks" } -}`, - }, - { - key: "$graphLookup", - template: `{ - "from": "", - "startWith": "", - "connectFromField": "", - "connectToField": "", - "as": "", - "maxDepth": 1, - "depthField": "", - "restrictSearchWithMatch": {} -}`, - }, - { - key: "$group", - template: `{ - "_id": "" -}`, - }, - { - key: "$indexStats", - template: "{\n\t\n}", - }, - { - key: "$limit", - template: `1`, - }, - { - key: "$listLocalSessions", - template: `{\n\t\n}`, - }, - { - key: "$listSessions", - template: `{\n\t\n}`, - }, - { - key: "$lookup", - template: `{ - "from": "", - "localField": "", - "foreignField": "", - "as": "" -}`, - }, - { - key: "$match", - template: "{\n\t\n}", - }, - { - key: "$merge", - template: `{ - "into": {}, - "on": "_id", - "whenMatched": "replace", - "whenNotMatched": "insert" -}`, - }, - { - key: "$out", - template: `{ - "db": "", - "coll": "" -}`, - }, - { - key: "$planCacheStats", - template: "{\n\t\n}", - }, - { - key: "$project", - template: "{\n\t\n}", - }, - { - key: "$redact", - template: "", - }, - { - key: "$replaceRoot", - template: `{ "newRoot": "" }`, - }, - { - key: "$replaceWith", - template: ``, - }, - { - key: "$sample", - template: `{ "size": 3 }`, - }, - { - key: "$set", - template: "{\n\t\n}", - }, - { - key: "$setWindowFields", - template: `{ - "partitionBy": "", - "sortBy": {}, - "output": {} -}`, - }, - { - key: "$skip", - template: `1`, - }, - { - key: "$sort", - template: "{\n\t\n}", - }, - { - key: "$sortByCount", - template: "", - }, - { - key: "$unionWith", - template: `{ - "coll": "", - "pipeline": [] -}`, - }, - { - key: "$unset", - template: "", - }, - { - key: "$unwind", - template: `{ - "path": "", - "includeArrayIndex": "", - "preserveNullAndEmptyArrays": true -}`, - }, - ], - }, - }, - extra: { - collection: { - displayName: "Collection", - type: DatasourceFieldType.STRING, - required: true, - }, - actionType: { - displayName: "Query Type", - type: DatasourceFieldType.LIST, - required: true, - data: { - read: ["find", "findOne", "findOneAndUpdate", "count", "distinct"], - create: ["insertOne", "insertMany"], - update: ["updateOne", "updateMany"], - delete: ["deleteOne", "deleteMany"], - aggregate: ["json", "pipeline"], +const getSchema = () => { + let schema = { + docs: "https://github.com/mongodb/node-mongodb-native", + friendlyName: "MongoDB", + type: "Non-relational", + description: + "MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era.", + datasource: { + connectionString: { + type: DatasourceFieldType.STRING, + required: true, + default: "mongodb://localhost:27017", + display: "Connection string", + }, + db: { + type: DatasourceFieldType.STRING, + required: true, + display: "DB", }, }, - }, + query: { + create: { + type: QueryType.JSON, + }, + read: { + type: QueryType.JSON, + }, + update: { + type: QueryType.JSON, + }, + delete: { + type: QueryType.JSON, + }, + aggregate: { + type: QueryType.JSON, + readable: true, + steps: [ + { + key: "$addFields", + template: "{\n\t\n}", + }, + { + key: "$bucket", + template: `{ + "groupBy": "", + "boundaries": [], + "default": "", + "output": {} + }`, + }, + { + key: "$bucketAuto", + template: `{ + "groupBy": "", + "buckets": 1, + "output": {}, + "granularity": "R5" + }`, + }, + { + key: "$changeStream", + template: `{ + "allChangesForCluster": true, + "fullDocument": "", + "fullDocumentBeforeChange": "", + "resumeAfter": 1, + "showExpandedEvents": true, + "startAfter": {}, + "startAtOperationTime": "" + }`, + }, + { + key: "$collStats", + template: `{ + "latencyStats": { "histograms": true } }, + "storageStats": { "scale": 1 } }, + "count": {}, + "queryExecStats": {} + }`, + }, + { + key: "$count", + template: ``, + }, + { + key: "$densify", + template: `{ + "field": "", + "partitionByFields": [], + "range": { + "step": 1, + "unit": 1, + "bounds": "full" + } + }`, + }, + { + key: "$documents", + template: `[]`, + }, + { + key: "$facet", + template: `{\n\t\n}`, + }, + { + key: "$fill", + template: `{ + "partitionBy": "", + "partitionByFields": [], + "sortBy": {}, + "output": {} + }`, + }, + { + key: "$geoNear", + template: `{ + "near": { + "type": "Point", + "coordinates": [ + -73.98142, 40.71782 + ] + }, + "key": "location", + "distanceField": "dist.calculated", + "query": { "category": "Parks" } + }`, + }, + { + key: "$graphLookup", + template: `{ + "from": "", + "startWith": "", + "connectFromField": "", + "connectToField": "", + "as": "", + "maxDepth": 1, + "depthField": "", + "restrictSearchWithMatch": {} + }`, + }, + { + key: "$group", + template: `{ + "_id": "" + }`, + }, + { + key: "$indexStats", + template: "{\n\t\n}", + }, + { + key: "$limit", + template: `1`, + }, + { + key: "$listLocalSessions", + template: `{\n\t\n}`, + }, + { + key: "$listSessions", + template: `{\n\t\n}`, + }, + { + key: "$lookup", + template: `{ + "from": "", + "localField": "", + "foreignField": "", + "as": "" + }`, + }, + { + key: "$match", + template: "{\n\t\n}", + }, + { + key: "$merge", + template: `{ + "into": {}, + "on": "_id", + "whenMatched": "replace", + "whenNotMatched": "insert" + }`, + }, + { + key: "$out", + template: `{ + "db": "", + "coll": "" + }`, + }, + { + key: "$planCacheStats", + template: "{\n\t\n}", + }, + { + key: "$project", + template: "{\n\t\n}", + }, + { + key: "$redact", + template: "", + }, + { + key: "$replaceRoot", + template: `{ "newRoot": "" }`, + }, + { + key: "$replaceWith", + template: ``, + }, + { + key: "$sample", + template: `{ "size": 3 }`, + }, + { + key: "$set", + template: "{\n\t\n}", + }, + { + key: "$setWindowFields", + template: `{ + "partitionBy": "", + "sortBy": {}, + "output": {} + }`, + }, + { + key: "$skip", + template: `1`, + }, + { + key: "$sort", + template: "{\n\t\n}", + }, + { + key: "$sortByCount", + template: "", + }, + { + key: "$unionWith", + template: `{ + "coll": "", + "pipeline": [] + }`, + }, + { + key: "$unset", + template: "", + }, + { + key: "$unwind", + template: `{ + "path": "", + "includeArrayIndex": "", + "preserveNullAndEmptyArrays": true + }`, + }, + ], + }, + }, + extra: { + collection: { + displayName: "Collection", + type: DatasourceFieldType.STRING, + required: true, + }, + actionType: { + displayName: "Query Type", + type: DatasourceFieldType.LIST, + required: true, + data: { + read: ["find", "findOne", "findOneAndUpdate", "count", "distinct"], + create: ["insertOne", "insertMany"], + update: ["updateOne", "updateMany"], + delete: ["deleteOne", "deleteMany"], + aggregate: ["json", "pipeline"], + }, + }, + }, + } + if (environment.SELF_HOSTED) { + schema.datasource = { + ...schema.datasource, + //@ts-ignore + tls: { + type: DatasourceFieldType.FIELD_GROUP, + display: "Configure SSL", + fields: { + tlsCertificateFile: { + type: DatasourceFieldType.STRING, + required: false, + display: "Certificate file path", + }, + tlsCertificateKeyFile: { + type: DatasourceFieldType.STRING, + required: false, + display: "Certificate Key file path", + }, + tlsCAFile: { + type: DatasourceFieldType.STRING, + required: false, + display: "CA file path", + }, + }, + }, + } + } + return schema } +const SCHEMA: Integration = getSchema() + class MongoIntegration implements IntegrationBase { private config: MongoDBConfig private client: any constructor(config: MongoDBConfig) { this.config = config - this.client = new MongoClient(config.connectionString) + const options: MongoClientOptions = { + tlsCertificateFile: config.tlsCertificateFile || undefined, + tlsCertificateKeyFile: config.tlsCertificateKeyFile || undefined, + tlsCAFile: config.tlsCAFile || undefined, + } + this.client = new MongoClient(config.connectionString, options) } async connect() { diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index 279a76446f..1847725c9d 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -33,6 +33,7 @@ export enum DatasourceFieldType { OBJECT = "object", JSON = "json", FILE = "file", + FIELD_GROUP = "fieldGroup", } export enum SourceName { @@ -95,6 +96,16 @@ export interface ExtraQueryConfig { } } +export interface DatasourceConfig { + [key: string]: { + type: string + display?: string + required?: boolean + default?: any + deprecated?: boolean + } +} + export interface Integration { docs: string plus?: boolean @@ -104,7 +115,7 @@ export interface Integration { friendlyName: string type?: string iconUrl?: string - datasource: {} + datasource: DatasourceConfig query: { [key: string]: QueryDefinition }