Merge pull request #5690 from Budibase/fix/mike-fixes-may

April release fixes - MySQL dates and various form/automation fixes
This commit is contained in:
Michael Drury 2022-05-03 16:51:07 +01:00 committed by GitHub
commit 0c10a9f7be
15 changed files with 126 additions and 79 deletions

View File

@ -2,17 +2,22 @@
import dayjs from "dayjs" import dayjs from "dayjs"
export let value export let value
export let schema
// adding the 0- will turn a string like 00:00:00 into a valid ISO // adding the 0- will turn a string like 00:00:00 into a valid ISO
// date, but will make actual ISO dates invalid // date, but will make actual ISO dates invalid
$: time = new Date(`0-${value}`) $: time = new Date(`0-${value}`)
$: isTime = !isNaN(time) $: isTimeOnly = !isNaN(time) || schema?.timeOnly
$: isDateOnly = schema?.dateOnly
$: format = isTimeOnly
? "HH:mm:ss"
: isDateOnly
? "MMMM D YYYY"
: "MMMM D YYYY, HH:mm"
</script> </script>
<div> <div>
{dayjs(isTime ? time : value).format( {dayjs(isTimeOnly ? time : value).format(format)}
isTime ? "HH:mm:ss" : "MMMM D YYYY, HH:mm"
)}
</div> </div>
<style> <style>

View File

@ -37,7 +37,7 @@
$: label = meta.name ? capitalise(meta.name) : "" $: label = meta.name ? capitalise(meta.name) : ""
const timeStamp = resolveTimeStamp(value) const timeStamp = resolveTimeStamp(value)
const isTimeStamp = !!timeStamp const isTimeStamp = !!timeStamp || meta?.timeOnly
</script> </script>
{#if type === "options" && meta.constraints.inclusion.length !== 0} {#if type === "options" && meta.constraints.inclusion.length !== 0}
@ -49,7 +49,12 @@
sort sort
/> />
{:else if type === "datetime"} {:else if type === "datetime"}
<DatePicker {label} timeOnly={isTimeStamp} bind:value /> <DatePicker
{label}
timeOnly={isTimeStamp}
enableTime={!meta?.dateOnly}
bind:value
/>
{:else if type === "attachment"} {:else if type === "attachment"}
<Dropzone {label} bind:value /> <Dropzone {label} bind:value />
{:else if type === "boolean"} {:else if type === "boolean"}

View File

@ -49,6 +49,10 @@
filters = [...filters, duplicate] filters = [...filters, duplicate]
} }
const getSchema = filter => {
return schemaFields.find(field => field.name === filter.field)
}
const onFieldChange = (expression, field) => { const onFieldChange = (expression, field) => {
// Update the field type // Update the field type
expression.type = enrichedSchemaFields.find(x => x.name === field)?.type expression.type = enrichedSchemaFields.find(x => x.name === field)?.type
@ -150,7 +154,12 @@
bind:value={filter.value} bind:value={filter.value}
/> />
{:else if filter.type === "datetime"} {:else if filter.type === "datetime"}
<DatePicker disabled={filter.noValue} bind:value={filter.value} /> <DatePicker
disabled={filter.noValue}
enableTime={!getSchema(filter).dateOnly}
timeOnly={getSchema(filter).timeOnly}
bind:value={filter.value}
/>
{:else} {:else}
<DrawerBindableInput disabled /> <DrawerBindableInput disabled />
{/if} {/if}

View File

@ -88,6 +88,10 @@
const schema = schemaFields.find(x => x.name === field) const schema = schemaFields.find(x => x.name === field)
return schema?.constraints?.inclusion || [] return schema?.constraints?.inclusion || []
} }
const getSchema = filter => {
return schemaFields.find(field => field.name === filter.field)
}
</script> </script>
<div class="container" class:mobile={$context.device.mobile}> <div class="container" class:mobile={$context.device.mobile}>
@ -134,7 +138,12 @@
bind:value={filter.value} bind:value={filter.value}
/> />
{:else if filter.type === "datetime"} {:else if filter.type === "datetime"}
<DatePicker disabled={filter.noValue} bind:value={filter.value} /> <DatePicker
disabled={filter.noValue}
enableTime={!getSchema(filter).dateOnly}
timeOnly={getSchema(filter).timeOnly}
bind:value={filter.value}
/>
{:else} {:else}
<Input disabled /> <Input disabled />
{/if} {/if}

View File

@ -22,7 +22,7 @@
if ( if (
formContext && formContext &&
$builderStore.inBuilder && $builderStore.inBuilder &&
$componentStore.selectedComponentPath?.includes($component.id) $componentStore?.selectedComponentPath?.includes($component.id)
) { ) {
formContext.formApi.setStep(step) formContext.formApi.setStep(step)
} }

View File

@ -242,12 +242,10 @@ module MSSQLModule {
if (typeof name !== "string") { if (typeof name !== "string") {
continue continue
} }
const type: string = convertSqlType(def.DATA_TYPE)
schema[name] = { schema[name] = {
autocolumn: !!autoColumns.find((col: string) => col === name), autocolumn: !!autoColumns.find((col: string) => col === name),
name: name, name: name,
type, ...convertSqlType(def.DATA_TYPE),
} }
} }
tables[tableName] = { tables[tableName] = {

View File

@ -15,6 +15,7 @@ import {
} from "./utils" } from "./utils"
import { DatasourcePlus } from "./base/datasourcePlus" import { DatasourcePlus } from "./base/datasourcePlus"
import dayjs from "dayjs" import dayjs from "dayjs"
import { FieldTypes } from "../constants"
const { NUMBER_REGEX } = require("../utilities") const { NUMBER_REGEX } = require("../utilities")
module MySQLModule { module MySQLModule {
@ -101,7 +102,7 @@ module MySQLModule {
} }
// if not a number, see if it is a date - important to do in this order as any // if not a number, see if it is a date - important to do in this order as any
// integer will be considered a valid date // integer will be considered a valid date
else if (dayjs(binding).isValid()) { else if (/^\d/.test(binding) && dayjs(binding).isValid()) {
bindings[i] = dayjs(binding).toDate() bindings[i] = dayjs(binding).toDate()
} }
} }
@ -151,20 +152,24 @@ module MySQLModule {
async internalQuery( async internalQuery(
query: SqlQuery, query: SqlQuery,
connect: boolean = true opts: { connect?: boolean; disableCoercion?: boolean } = {
connect: true,
disableCoercion: false,
}
): Promise<any[] | any> { ): Promise<any[] | any> {
try { try {
if (connect) { if (opts?.connect) {
await this.connect() await this.connect()
} }
const baseBindings = query.bindings || []
const bindings = opts?.disableCoercion
? baseBindings
: bindingTypeCoerce(baseBindings)
// Node MySQL is callback based, so we must wrap our call in a promise // Node MySQL is callback based, so we must wrap our call in a promise
const response = await this.client.query( const response = await this.client.query(query.sql, bindings)
query.sql,
bindingTypeCoerce(query.bindings || [])
)
return response[0] return response[0]
} finally { } finally {
if (connect) { if (opts?.connect) {
await this.disconnect() await this.disconnect()
} }
} }
@ -179,7 +184,7 @@ module MySQLModule {
// get the tables first // get the tables first
const tablesResp = await this.internalQuery( const tablesResp = await this.internalQuery(
{ sql: "SHOW TABLES;" }, { sql: "SHOW TABLES;" },
false { connect: false }
) )
const tableNames = tablesResp.map( const tableNames = tablesResp.map(
(obj: any) => (obj: any) =>
@ -191,7 +196,7 @@ module MySQLModule {
const schema: TableSchema = {} const schema: TableSchema = {}
const descResp = await this.internalQuery( const descResp = await this.internalQuery(
{ sql: `DESCRIBE \`${tableName}\`;` }, { sql: `DESCRIBE \`${tableName}\`;` },
false { connect: false }
) )
for (let column of descResp) { for (let column of descResp) {
const columnName = column.Field const columnName = column.Field
@ -211,8 +216,8 @@ module MySQLModule {
schema[columnName] = { schema[columnName] = {
name: columnName, name: columnName,
autocolumn: isAuto, autocolumn: isAuto,
type: convertSqlType(column.Type),
constraints, constraints,
...convertSqlType(column.Type),
} }
} }
if (!tables[tableName]) { if (!tables[tableName]) {
@ -254,7 +259,8 @@ module MySQLModule {
async query(json: QueryJson) { async query(json: QueryJson) {
await this.connect() await this.connect()
try { try {
const queryFn = (query: any) => this.internalQuery(query, false) const queryFn = (query: any) =>
this.internalQuery(query, { connect: false, disableCoercion: true })
return await this.queryWithReturning(json, queryFn) return await this.queryWithReturning(json, queryFn)
} finally { } finally {
await this.disconnect() await this.disconnect()

View File

@ -279,9 +279,9 @@ module OracleModule {
) )
} }
private internalConvertType(column: OracleColumn): string { private internalConvertType(column: OracleColumn): { type: string } {
if (this.isBooleanType(column)) { if (this.isBooleanType(column)) {
return FieldTypes.BOOLEAN return { type: FieldTypes.BOOLEAN }
} }
return convertSqlType(column.type) return convertSqlType(column.type)
@ -328,7 +328,7 @@ module OracleModule {
fieldSchema = { fieldSchema = {
autocolumn: OracleIntegration.isAutoColumn(oracleColumn), autocolumn: OracleIntegration.isAutoColumn(oracleColumn),
name: columnName, name: columnName,
type: this.internalConvertType(oracleColumn), ...this.internalConvertType(oracleColumn),
} }
table.schema[columnName] = fieldSchema table.schema[columnName] = fieldSchema
} }

View File

@ -227,7 +227,6 @@ module PostgresModule {
} }
} }
const type: string = convertSqlType(column.data_type)
const identity = !!( const identity = !!(
column.identity_generation || column.identity_generation ||
column.identity_start || column.identity_start ||
@ -242,7 +241,7 @@ module PostgresModule {
tables[tableName].schema[columnName] = { tables[tableName].schema[columnName] = {
autocolumn: isAuto, autocolumn: isAuto,
name: columnName, name: columnName,
type, ...convertSqlType(column.data_type),
} }
} }

View File

@ -35,6 +35,9 @@ const SQL_DATE_TYPE_MAP = {
date: FieldTypes.DATETIME, date: FieldTypes.DATETIME,
} }
const SQL_DATE_ONLY_TYPES = ["date"]
const SQL_TIME_ONLY_TYPES = ["time"]
const SQL_STRING_TYPE_MAP = { const SQL_STRING_TYPE_MAP = {
varchar: FieldTypes.STRING, varchar: FieldTypes.STRING,
char: FieldTypes.STRING, char: FieldTypes.STRING,
@ -85,9 +88,9 @@ export function breakExternalTableId(tableId: string | undefined) {
return {} return {}
} }
const parts = tableId.split(DOUBLE_SEPARATOR) const parts = tableId.split(DOUBLE_SEPARATOR)
let tableName = parts.pop() let datasourceId = parts.shift()
// if they need joined // if they need joined
let datasourceId = parts.join(DOUBLE_SEPARATOR) let tableName = parts.join(DOUBLE_SEPARATOR)
return { datasourceId, tableName } return { datasourceId, tableName }
} }
@ -137,12 +140,20 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
} }
export function convertSqlType(type: string) { export function convertSqlType(type: string) {
let foundType = FieldTypes.STRING
const lcType = type.toLowerCase()
for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) { for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) {
if (type.toLowerCase().includes(external)) { if (lcType.includes(external)) {
return internal foundType = internal
break
} }
} }
return FieldTypes.STRING const schema: any = { type: foundType }
if (foundType === FieldTypes.DATETIME) {
schema.dateOnly = SQL_DATE_ONLY_TYPES.includes(lcType)
schema.timeOnly = SQL_TIME_ONLY_TYPES.includes(lcType)
}
return schema
} }
export function getSqlQuery(query: SqlQuery | string): SqlQuery { export function getSqlQuery(query: SqlQuery | string): SqlQuery {

View File

@ -100,10 +100,10 @@ class Orchestrator {
let automation = this._automation let automation = this._automation
const app = await this.getApp() const app = await this.getApp()
let stopped = false let stopped = false
let loopStep let loopStep = null
let stepCount = 0 let stepCount = 0
let loopStepNumber let loopStepNumber = null
let loopSteps = [] let loopSteps = []
for (let step of automation.definition.steps) { for (let step of automation.definition.steps) {
stepCount++ stepCount++
@ -286,18 +286,16 @@ class Orchestrator {
module.exports = (input, callback) => { module.exports = (input, callback) => {
const appId = input.data.event.appId const appId = input.data.event.appId
doInAppContext(appId, () => { doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator( const automationOrchestrator = new Orchestrator(
input.data.automation, input.data.automation,
input.data.event input.data.event
) )
automationOrchestrator try {
.execute() const response = await automationOrchestrator.execute()
.then(response => {
callback(null, response) callback(null, response)
}) } catch (err) {
.catch(err => {
callback(err) callback(err)
}) }
}) })
} }

View File

@ -191,14 +191,13 @@ class QueryRunner {
} }
module.exports = (input, callback) => { module.exports = (input, callback) => {
doInAppContext(input.appId, () => { doInAppContext(input.appId, async () => {
const Runner = new QueryRunner(input) const Runner = new QueryRunner(input)
Runner.execute() try {
.then(response => { const response = await Runner.execute()
callback(null, response) callback(null, response)
}) } catch (err) {
.catch(err => {
callback(err) callback(err)
}) }
}) })
} }

View File

@ -2,7 +2,11 @@ const { budibaseTempDir } = require("../budibaseDir")
const fs = require("fs") const fs = require("fs")
const { join } = require("path") const { join } = require("path")
const uuid = require("uuid/v4") const uuid = require("uuid/v4")
const { doWithDB } = require("@budibase/backend-core/db") const {
doWithDB,
dangerousGetDB,
closeDB,
} = require("@budibase/backend-core/db")
const { ObjectStoreBuckets } = require("../../constants") const { ObjectStoreBuckets } = require("../../constants")
const { const {
upload, upload,
@ -151,14 +155,18 @@ exports.streamBackup = async appId => {
* @return {*} either a readable stream or a string * @return {*} either a readable stream or a string
*/ */
exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => { exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => {
return doWithDB(dbName, async db => { // streaming a DB dump is a bit more complicated, can't close DB
// Stream the dump if required
if (stream) { if (stream) {
const db = dangerousGetDB(dbName)
const memStream = new MemoryStream() const memStream = new MemoryStream()
memStream.on("end", async () => {
await closeDB(db)
})
db.dump(memStream, { filter }) db.dump(memStream, { filter })
return memStream return memStream
} }
return doWithDB(dbName, async db => {
// Write the dump to file if required // Write the dump to file if required
if (exportName) { if (exportName) {
const path = join(budibaseTempDir(), exportName) const path = join(budibaseTempDir(), exportName)

View File

@ -1014,10 +1014,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.127": "@budibase/backend-core@1.0.135":
version "1.0.127" version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.127.tgz#5d1f4b18b31436ddb770dc1ddf16f201ec95dda7" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.135.tgz#b8cf217243b285c6e74435acba7c8960a1651c23"
integrity sha512-3INFkAIxL0Q8Sa65ELRGQqPs+4baykKyb1z/XuO1MyuDPnbFKXGOjl1V61EMy622gsmLk90IJL6aQfh3Grwhvw== integrity sha512-7gnTW6w8upJSPNqdyt5gpkubf0glDyTiSszLVVRZdxJypYAnBjcVrSFvlNPDzI4X2glnNnGcXBxFdDg6Z9ZS5w==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -1091,12 +1091,12 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/pro@1.0.127": "@budibase/pro@1.0.135":
version "1.0.127" version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.127.tgz#a8bcffb8ccc6afde64370b3a3dc22e2d0dd04f46" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.135.tgz#fa4f3b2a8e14905c97305a79e4b91d1094dba1a0"
integrity sha512-dj0SFTmO8JuMQ97/Ik6jVPQsh9AW7U5Wkgpa4yeNfwWw3DvSoktCxpeZ9mND6BR/DWTaeZljFKsQ6uk6nimzdQ== integrity sha512-XfZTfrplyY03zuTE3dPzdJvVD3qO5ShbRUYNX/05VBubwhS7S0mczwfCTJPi8agfx3LGN4yQd7GhFbaN2zrOPg==
dependencies: dependencies:
"@budibase/backend-core" "1.0.127" "@budibase/backend-core" "1.0.135"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@budibase/standard-components@^0.9.139": "@budibase/standard-components@^0.9.139":

View File

@ -293,10 +293,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.127": "@budibase/backend-core@1.0.135":
version "1.0.127" version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.127.tgz#5d1f4b18b31436ddb770dc1ddf16f201ec95dda7" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.135.tgz#b8cf217243b285c6e74435acba7c8960a1651c23"
integrity sha512-3INFkAIxL0Q8Sa65ELRGQqPs+4baykKyb1z/XuO1MyuDPnbFKXGOjl1V61EMy622gsmLk90IJL6aQfh3Grwhvw== integrity sha512-7gnTW6w8upJSPNqdyt5gpkubf0glDyTiSszLVVRZdxJypYAnBjcVrSFvlNPDzI4X2glnNnGcXBxFdDg6Z9ZS5w==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -321,12 +321,12 @@
uuid "^8.3.2" uuid "^8.3.2"
zlib "^1.0.5" zlib "^1.0.5"
"@budibase/pro@1.0.127": "@budibase/pro@1.0.135":
version "1.0.127" version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.127.tgz#a8bcffb8ccc6afde64370b3a3dc22e2d0dd04f46" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.135.tgz#fa4f3b2a8e14905c97305a79e4b91d1094dba1a0"
integrity sha512-dj0SFTmO8JuMQ97/Ik6jVPQsh9AW7U5Wkgpa4yeNfwWw3DvSoktCxpeZ9mND6BR/DWTaeZljFKsQ6uk6nimzdQ== integrity sha512-XfZTfrplyY03zuTE3dPzdJvVD3qO5ShbRUYNX/05VBubwhS7S0mczwfCTJPi8agfx3LGN4yQd7GhFbaN2zrOPg==
dependencies: dependencies:
"@budibase/backend-core" "1.0.127" "@budibase/backend-core" "1.0.135"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@cspotcode/source-map-consumer@0.8.0": "@cspotcode/source-map-consumer@0.8.0":