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"
export let value
export let schema
// adding the 0- will turn a string like 00:00:00 into a valid ISO
// date, but will make actual ISO dates invalid
$: 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>
<div>
{dayjs(isTime ? time : value).format(
isTime ? "HH:mm:ss" : "MMMM D YYYY, HH:mm"
)}
{dayjs(isTimeOnly ? time : value).format(format)}
</div>
<style>

View File

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

View File

@ -49,6 +49,10 @@
filters = [...filters, duplicate]
}
const getSchema = filter => {
return schemaFields.find(field => field.name === filter.field)
}
const onFieldChange = (expression, field) => {
// Update the field type
expression.type = enrichedSchemaFields.find(x => x.name === field)?.type
@ -150,7 +154,12 @@
bind:value={filter.value}
/>
{: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}
<DrawerBindableInput disabled />
{/if}

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import {
} from "./utils"
import { DatasourcePlus } from "./base/datasourcePlus"
import dayjs from "dayjs"
import { FieldTypes } from "../constants"
const { NUMBER_REGEX } = require("../utilities")
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
// 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()
}
}
@ -151,20 +152,24 @@ module MySQLModule {
async internalQuery(
query: SqlQuery,
connect: boolean = true
opts: { connect?: boolean; disableCoercion?: boolean } = {
connect: true,
disableCoercion: false,
}
): Promise<any[] | any> {
try {
if (connect) {
if (opts?.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
const response = await this.client.query(
query.sql,
bindingTypeCoerce(query.bindings || [])
)
const response = await this.client.query(query.sql, bindings)
return response[0]
} finally {
if (connect) {
if (opts?.connect) {
await this.disconnect()
}
}
@ -179,7 +184,7 @@ module MySQLModule {
// get the tables first
const tablesResp = await this.internalQuery(
{ sql: "SHOW TABLES;" },
false
{ connect: false }
)
const tableNames = tablesResp.map(
(obj: any) =>
@ -191,7 +196,7 @@ module MySQLModule {
const schema: TableSchema = {}
const descResp = await this.internalQuery(
{ sql: `DESCRIBE \`${tableName}\`;` },
false
{ connect: false }
)
for (let column of descResp) {
const columnName = column.Field
@ -211,8 +216,8 @@ module MySQLModule {
schema[columnName] = {
name: columnName,
autocolumn: isAuto,
type: convertSqlType(column.Type),
constraints,
...convertSqlType(column.Type),
}
}
if (!tables[tableName]) {
@ -254,7 +259,8 @@ module MySQLModule {
async query(json: QueryJson) {
await this.connect()
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)
} finally {
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)) {
return FieldTypes.BOOLEAN
return { type: FieldTypes.BOOLEAN }
}
return convertSqlType(column.type)
@ -328,7 +328,7 @@ module OracleModule {
fieldSchema = {
autocolumn: OracleIntegration.isAutoColumn(oracleColumn),
name: columnName,
type: this.internalConvertType(oracleColumn),
...this.internalConvertType(oracleColumn),
}
table.schema[columnName] = fieldSchema
}

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,11 @@ const { budibaseTempDir } = require("../budibaseDir")
const fs = require("fs")
const { join } = require("path")
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 {
upload,
@ -151,14 +155,18 @@ exports.streamBackup = async appId => {
* @return {*} either a readable stream or a string
*/
exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => {
return doWithDB(dbName, async db => {
// Stream the dump if required
if (stream) {
const memStream = new MemoryStream()
db.dump(memStream, { filter })
return memStream
}
// streaming a DB dump is a bit more complicated, can't close DB
if (stream) {
const db = dangerousGetDB(dbName)
const memStream = new MemoryStream()
memStream.on("end", async () => {
await closeDB(db)
})
db.dump(memStream, { filter })
return memStream
}
return doWithDB(dbName, async db => {
// Write the dump to file if required
if (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"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.127":
version "1.0.127"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.127.tgz#5d1f4b18b31436ddb770dc1ddf16f201ec95dda7"
integrity sha512-3INFkAIxL0Q8Sa65ELRGQqPs+4baykKyb1z/XuO1MyuDPnbFKXGOjl1V61EMy622gsmLk90IJL6aQfh3Grwhvw==
"@budibase/backend-core@1.0.135":
version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.135.tgz#b8cf217243b285c6e74435acba7c8960a1651c23"
integrity sha512-7gnTW6w8upJSPNqdyt5gpkubf0glDyTiSszLVVRZdxJypYAnBjcVrSFvlNPDzI4X2glnNnGcXBxFdDg6Z9ZS5w==
dependencies:
"@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0"
@ -1091,12 +1091,12 @@
svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0"
"@budibase/pro@1.0.127":
version "1.0.127"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.127.tgz#a8bcffb8ccc6afde64370b3a3dc22e2d0dd04f46"
integrity sha512-dj0SFTmO8JuMQ97/Ik6jVPQsh9AW7U5Wkgpa4yeNfwWw3DvSoktCxpeZ9mND6BR/DWTaeZljFKsQ6uk6nimzdQ==
"@budibase/pro@1.0.135":
version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.135.tgz#fa4f3b2a8e14905c97305a79e4b91d1094dba1a0"
integrity sha512-XfZTfrplyY03zuTE3dPzdJvVD3qO5ShbRUYNX/05VBubwhS7S0mczwfCTJPi8agfx3LGN4yQd7GhFbaN2zrOPg==
dependencies:
"@budibase/backend-core" "1.0.127"
"@budibase/backend-core" "1.0.135"
node-fetch "^2.6.1"
"@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"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.127":
version "1.0.127"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.127.tgz#5d1f4b18b31436ddb770dc1ddf16f201ec95dda7"
integrity sha512-3INFkAIxL0Q8Sa65ELRGQqPs+4baykKyb1z/XuO1MyuDPnbFKXGOjl1V61EMy622gsmLk90IJL6aQfh3Grwhvw==
"@budibase/backend-core@1.0.135":
version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.135.tgz#b8cf217243b285c6e74435acba7c8960a1651c23"
integrity sha512-7gnTW6w8upJSPNqdyt5gpkubf0glDyTiSszLVVRZdxJypYAnBjcVrSFvlNPDzI4X2glnNnGcXBxFdDg6Z9ZS5w==
dependencies:
"@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0"
@ -321,12 +321,12 @@
uuid "^8.3.2"
zlib "^1.0.5"
"@budibase/pro@1.0.127":
version "1.0.127"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.127.tgz#a8bcffb8ccc6afde64370b3a3dc22e2d0dd04f46"
integrity sha512-dj0SFTmO8JuMQ97/Ik6jVPQsh9AW7U5Wkgpa4yeNfwWw3DvSoktCxpeZ9mND6BR/DWTaeZljFKsQ6uk6nimzdQ==
"@budibase/pro@1.0.135":
version "1.0.135"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.135.tgz#fa4f3b2a8e14905c97305a79e4b91d1094dba1a0"
integrity sha512-XfZTfrplyY03zuTE3dPzdJvVD3qO5ShbRUYNX/05VBubwhS7S0mczwfCTJPi8agfx3LGN4yQd7GhFbaN2zrOPg==
dependencies:
"@budibase/backend-core" "1.0.127"
"@budibase/backend-core" "1.0.135"
node-fetch "^2.6.1"
"@cspotcode/source-map-consumer@0.8.0":