Merge branch 'master' of github.com:Budibase/budibase into v3-ui
This commit is contained in:
commit
bae5640e90
|
@ -3,26 +3,50 @@ name: deploy-featurebranch
|
|||
on:
|
||||
pull_request:
|
||||
types: [
|
||||
labeled,
|
||||
# default types below (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request)
|
||||
opened,
|
||||
synchronize,
|
||||
reopened,
|
||||
]
|
||||
labeled,
|
||||
# default types below (https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request)
|
||||
opened,
|
||||
synchronize,
|
||||
reopened,
|
||||
]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: |
|
||||
(github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase') &&
|
||||
contains(github.event.pull_request.labels.*.name, 'feature-branch')
|
||||
(
|
||||
contains(github.event.pull_request.labels.*.name, 'feature-branch') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'feature-branch-pro') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'feature-branch-team') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'feature-branch-business') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'feature-branch-enterprise')
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set PAYLOAD_LICENSE_TYPE
|
||||
id: set_license_type
|
||||
run: |
|
||||
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch') }}" == "true" ]]; then
|
||||
echo "PAYLOAD_LICENSE_TYPE=free" >> $GITHUB_ENV
|
||||
elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-pro') }}" == "true" ]]; then
|
||||
echo "PAYLOAD_LICENSE_TYPE=pro" >> $GITHUB_ENV
|
||||
elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-team') }}" == "true" ]]; then
|
||||
echo "PAYLOAD_LICENSE_TYPE=team" >> $GITHUB_ENV
|
||||
elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-business') }}" == "true" ]]; then
|
||||
echo "PAYLOAD_LICENSE_TYPE=business" >> $GITHUB_ENV
|
||||
elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'feature-branch-enterprise') }}" == "true" ]]; then
|
||||
echo "PAYLOAD_LICENSE_TYPE=enterprise" >> $GITHUB_ENV
|
||||
else
|
||||
echo "PAYLOAD_LICENSE_TYPE=free" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- uses: passeidireto/trigger-external-workflow-action@main
|
||||
env:
|
||||
PAYLOAD_BRANCH: ${{ github.head_ref }}
|
||||
PAYLOAD_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
PAYLOAD_LICENSE_TYPE: "free"
|
||||
PAYLOAD_LICENSE_TYPE: ${{ env.PAYLOAD_LICENSE_TYPE }}
|
||||
with:
|
||||
repository: budibase/budibase-deploys
|
||||
event: featurebranch-qa-deploy
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "2.32.16",
|
||||
"version": "2.32.17",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*",
|
||||
|
|
|
@ -273,6 +273,7 @@ class InternalBuilder {
|
|||
const col = parts.pop()!
|
||||
const schema = this.table.schema[col]
|
||||
let identifier = this.quotedIdentifier(field)
|
||||
|
||||
if (
|
||||
schema.type === FieldType.STRING ||
|
||||
schema.type === FieldType.LONGFORM ||
|
||||
|
@ -957,6 +958,13 @@ class InternalBuilder {
|
|||
return query
|
||||
}
|
||||
|
||||
isAggregateField(field: string): boolean {
|
||||
const found = this.query.resource?.aggregations?.find(
|
||||
aggregation => aggregation.name === field
|
||||
)
|
||||
return !!found
|
||||
}
|
||||
|
||||
addSorting(query: Knex.QueryBuilder): Knex.QueryBuilder {
|
||||
let { sort, resource } = this.query
|
||||
const primaryKey = this.table.primary
|
||||
|
@ -979,13 +987,17 @@ class InternalBuilder {
|
|||
nulls = value.direction === SortOrder.ASCENDING ? "first" : "last"
|
||||
}
|
||||
|
||||
let composite = `${aliased}.${key}`
|
||||
if (this.client === SqlClient.ORACLE) {
|
||||
query = query.orderByRaw(
|
||||
`${this.convertClobs(composite)} ${direction} nulls ${nulls}`
|
||||
)
|
||||
if (this.isAggregateField(key)) {
|
||||
query = query.orderBy(key, direction, nulls)
|
||||
} else {
|
||||
query = query.orderBy(composite, direction, nulls)
|
||||
let composite = `${aliased}.${key}`
|
||||
if (this.client === SqlClient.ORACLE) {
|
||||
query = query.orderByRaw(
|
||||
`${this.convertClobs(composite)} ${direction} nulls ${nulls}`
|
||||
)
|
||||
} else {
|
||||
query = query.orderBy(composite, direction, nulls)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,7 @@
|
|||
let candidateIndex
|
||||
let lastSearchId
|
||||
let searching = false
|
||||
let container
|
||||
let anchor
|
||||
let relationshipAnchor
|
||||
let relationshipFields
|
||||
|
||||
$: fieldValue = parseValue(value)
|
||||
$: oneRowOnly = schema?.relationshipType === "one-to-many"
|
||||
|
@ -57,13 +54,6 @@
|
|||
return acc
|
||||
}, {})
|
||||
|
||||
$: showRelationshipFields =
|
||||
relationshipAnchor &&
|
||||
relationshipFields &&
|
||||
Object.keys(relationshipFields).length &&
|
||||
focused &&
|
||||
!isOpen
|
||||
|
||||
const parseValue = value => {
|
||||
if (Array.isArray(value) && value.every(x => x?._id)) {
|
||||
return value
|
||||
|
@ -245,16 +235,6 @@
|
|||
return value
|
||||
}
|
||||
|
||||
const displayRelationshipFields = (e, relationship) => {
|
||||
relationshipAnchor = e.target
|
||||
relationshipFields = relationFields[relationship._id]
|
||||
}
|
||||
|
||||
const hideRelationshipFields = () => {
|
||||
relationshipAnchor = null
|
||||
relationshipFields = undefined
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
api = {
|
||||
focus: open,
|
||||
|
@ -274,7 +254,7 @@
|
|||
style="--color:{color};"
|
||||
bind:this={anchor}
|
||||
>
|
||||
<div class="container" bind:this={container}>
|
||||
<div class="container">
|
||||
<div
|
||||
class="values"
|
||||
class:wrap={editable || contentLines > 1}
|
||||
|
@ -286,9 +266,7 @@
|
|||
<div
|
||||
class="badge"
|
||||
class:extra-info={!!relationFields[relationship._id]}
|
||||
on:mouseenter={e => displayRelationshipFields(e, relationship)}
|
||||
on:focus={() => {}}
|
||||
on:mouseleave={() => hideRelationshipFields()}
|
||||
>
|
||||
<span>
|
||||
{readable(
|
||||
|
@ -363,26 +341,6 @@
|
|||
</GridPopover>
|
||||
{/if}
|
||||
|
||||
{#if showRelationshipFields}
|
||||
<GridPopover
|
||||
anchor={relationshipAnchor}
|
||||
maxWidth={400}
|
||||
offset={4}
|
||||
clickOutsideOverride
|
||||
>
|
||||
<div class="relationship-fields">
|
||||
{#each Object.entries(relationshipFields) as [fieldName, fieldValue]}
|
||||
<div class="relationship-field-name">
|
||||
{fieldName}
|
||||
</div>
|
||||
<div class="relationship-field-value">
|
||||
{fieldValue}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</GridPopover>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
flex: 1 1 auto;
|
||||
|
@ -547,29 +505,4 @@
|
|||
.search :global(.spectrum-Form-item) {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.relationship-fields {
|
||||
margin: var(--spacing-m) var(--spacing-l);
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-row-gap: var(--spacing-m);
|
||||
grid-column-gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.relationship-field-name {
|
||||
text-transform: uppercase;
|
||||
color: var(--spectrum-global-color-gray-600);
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 120px;
|
||||
}
|
||||
.relationship-field-value {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
line-clamp: 3;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit aca9828117bb97f54f40ee359f1a3f6e259174e7
|
||||
Subproject commit fc4c7f4925139af078480217965c3d6338dc0a7f
|
|
@ -3381,6 +3381,124 @@ describe.each([
|
|||
expect(rows).toHaveLength(1)
|
||||
expect(rows[0].sum).toEqual(3)
|
||||
})
|
||||
|
||||
it("should be able to sort by group by field", async () => {
|
||||
const table = await config.api.table.save(
|
||||
saveTableRequest({
|
||||
schema: {
|
||||
quantity: {
|
||||
type: FieldType.NUMBER,
|
||||
name: "quantity",
|
||||
},
|
||||
price: {
|
||||
type: FieldType.NUMBER,
|
||||
name: "price",
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
type: ViewV2Type.CALCULATION,
|
||||
schema: {
|
||||
quantity: { visible: true },
|
||||
sum: {
|
||||
visible: true,
|
||||
calculationType: CalculationType.SUM,
|
||||
field: "price",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await config.api.row.bulkImport(table._id!, {
|
||||
rows: [
|
||||
{
|
||||
quantity: 1,
|
||||
price: 1,
|
||||
},
|
||||
{
|
||||
quantity: 1,
|
||||
price: 2,
|
||||
},
|
||||
{
|
||||
quantity: 2,
|
||||
price: 10,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const { rows } = await config.api.viewV2.search(view.id, {
|
||||
query: {},
|
||||
sort: "quantity",
|
||||
sortOrder: SortOrder.DESCENDING,
|
||||
})
|
||||
|
||||
expect(rows).toEqual([
|
||||
expect.objectContaining({ quantity: 2, sum: 10 }),
|
||||
expect.objectContaining({ quantity: 1, sum: 3 }),
|
||||
])
|
||||
})
|
||||
|
||||
it("should be able to sort by a calculation", async () => {
|
||||
const table = await config.api.table.save(
|
||||
saveTableRequest({
|
||||
schema: {
|
||||
quantity: {
|
||||
type: FieldType.NUMBER,
|
||||
name: "quantity",
|
||||
},
|
||||
price: {
|
||||
type: FieldType.NUMBER,
|
||||
name: "price",
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
await config.api.row.bulkImport(table._id!, {
|
||||
rows: [
|
||||
{
|
||||
quantity: 1,
|
||||
price: 1,
|
||||
},
|
||||
{
|
||||
quantity: 1,
|
||||
price: 2,
|
||||
},
|
||||
{
|
||||
quantity: 2,
|
||||
price: 10,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
type: ViewV2Type.CALCULATION,
|
||||
schema: {
|
||||
quantity: { visible: true },
|
||||
sum: {
|
||||
visible: true,
|
||||
calculationType: CalculationType.SUM,
|
||||
field: "price",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { rows } = await config.api.viewV2.search(view.id, {
|
||||
query: {},
|
||||
sort: "sum",
|
||||
sortOrder: SortOrder.DESCENDING,
|
||||
})
|
||||
|
||||
expect(rows).toEqual([
|
||||
expect.objectContaining({ quantity: 2, sum: 10 }),
|
||||
expect.objectContaining({ quantity: 1, sum: 3 }),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
!isLucene &&
|
||||
|
|
|
@ -418,13 +418,26 @@ export async function search(
|
|||
|
||||
if (params.sort) {
|
||||
const sortField = table.schema[params.sort]
|
||||
const sortType =
|
||||
sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING
|
||||
request.sort = {
|
||||
[mapToUserColumn(sortField.name)]: {
|
||||
direction: params.sortOrder || SortOrder.ASCENDING,
|
||||
type: sortType as SortType,
|
||||
},
|
||||
const isAggregateField = aggregations.some(agg => agg.name === params.sort)
|
||||
|
||||
if (isAggregateField) {
|
||||
request.sort = {
|
||||
[params.sort]: {
|
||||
direction: params.sortOrder || SortOrder.ASCENDING,
|
||||
type: SortType.NUMBER,
|
||||
},
|
||||
}
|
||||
} else if (sortField) {
|
||||
const sortType =
|
||||
sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING
|
||||
request.sort = {
|
||||
[mapToUserColumn(sortField.name)]: {
|
||||
direction: params.sortOrder || SortOrder.ASCENDING,
|
||||
type: sortType as SortType,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unable to sort by ${params.sort}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -172,7 +172,7 @@ export const processSearchFilters = (
|
|||
.sort((a, b) => {
|
||||
return a.localeCompare(b)
|
||||
})
|
||||
.filter(key => key in filter)
|
||||
.filter(key => filter[key])
|
||||
|
||||
if (filterPropertyKeys.length == 1) {
|
||||
const key = filterPropertyKeys[0],
|
||||
|
|
Loading…
Reference in New Issue