Browse Source

Merge pull request #4506 from nocodb/develop

pull/4507/head 0.99.2
github-actions[bot] 2 years ago committed by GitHub
parent
commit
6d53af2f16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      packages/nc-gui/app.vue
  2. 1
      packages/nc-gui/components/smartsheet/header/CellIcon.ts
  3. 21
      packages/nc-gui/components/virtual-cell/Lookup.vue
  4. 2
      packages/nc-gui/layouts/default.vue
  5. 52
      packages/nc-gui/package-lock.json
  6. 2
      packages/nc-gui/package.json
  7. 4
      packages/nocodb-sdk/package-lock.json
  8. 30
      packages/nocodb/package-lock.json
  9. 2
      packages/nocodb/package.json
  10. 80
      packages/nocodb/src/lib/db/sql-client/lib/mssql/MssqlClient.ts
  11. 37
      packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts
  12. 9
      packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts

2
packages/nc-gui/app.vue

@ -36,7 +36,7 @@ if (typeof window !== 'undefined') {
<template>
<a-config-provider>
<NuxtLayout :name="disableBaseLayout ? false : 'base'">
<NuxtPage :transition="false" :key="key" />
<NuxtPage :key="key" :transition="false" />
</NuxtLayout>
</a-config-provider>
</template>

1
packages/nc-gui/components/smartsheet/header/CellIcon.ts

@ -19,7 +19,6 @@ import {
isJSON,
isPercent,
isPhoneNumber,
isPrimary,
isRating,
isSet,
isSingleSelect,

21
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -27,14 +27,6 @@ const meta = inject(MetaInj, ref())
const cellValue = inject(CellValueInj, ref())
const arrValue = computed(() => {
if (!cellValue.value) return []
if (Array.isArray(cellValue.value)) return cellValue.value
return [cellValue.value]
})
const relationColumn = computed(
() =>
meta.value?.columns?.find((c) => c.id === (column.value?.colOptions as LookupType)?.fk_relation_column_id) as
@ -66,6 +58,19 @@ const lookupColumn = computed(
| undefined,
)
const arrValue = computed(() => {
if (!cellValue.value) return []
// if lookup column is Attachment and relation type is Belongs to wrap the value in an array
// since the attachment component expects an array or JSON string array
if (lookupColumn.value?.uidt === UITypes.Attachment && relationColumn.value?.colOptions?.type === RelationTypes.BELONGS_TO)
return [cellValue.value]
if (Array.isArray(cellValue.value)) return cellValue.value
return [cellValue.value]
})
provide(MetaInj, lookupTableMeta)
provide(CellUrlDisableOverlayInj, ref(true))

2
packages/nc-gui/layouts/default.vue

@ -20,7 +20,7 @@ export default {
<template>
<div class="w-full h-full">
<Teleport :to="hasSidebar ? '#nc-sidebar-left' : null" :disabled="!hasSidebar">
<slot name="sidebar" />
<slot :key="$route.name" name="sidebar" />
</Teleport>
<a-layout-content>

52
packages/nc-gui/package-lock.json generated

@ -26,7 +26,7 @@
"jwt-decode": "^3.1.2",
"locale-codes": "^1.3.1",
"monaco-editor": "^0.33.0",
"nocodb-sdk": "0.99.1",
"nocodb-sdk": "file:../nocodb-sdk",
"papaparse": "^5.3.2",
"socket.io-client": "^4.5.1",
"sortablejs": "^1.15.0",
@ -86,7 +86,6 @@
},
"../nocodb-sdk": {
"version": "0.99.1",
"extraneous": true,
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -8391,6 +8390,7 @@
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
"devOptional": true,
"funding": [
{
"type": "individual",
@ -11748,21 +11748,8 @@
}
},
"node_modules/nocodb-sdk": {
"version": "0.99.1",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.99.1.tgz",
"integrity": "sha512-CfaYgqi4m5jFWaRazsshvLN468Trfq/DxXVAzLz9hIZv88urYbXJaAwbXTNR8q+CTpACabkN9NW02HL6Jr1LQQ==",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^1.3.6"
}
},
"node_modules/nocodb-sdk/node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
"resolved": "../nocodb-sdk",
"link": true
},
"node_modules/node-abi": {
"version": "3.23.0",
@ -23377,7 +23364,8 @@
"follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
"devOptional": true
},
"form-data": {
"version": "4.0.0",
@ -25847,22 +25835,22 @@
}
},
"nocodb-sdk": {
"version": "0.99.1",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.99.1.tgz",
"integrity": "sha512-CfaYgqi4m5jFWaRazsshvLN468Trfq/DxXVAzLz9hIZv88urYbXJaAwbXTNR8q+CTpACabkN9NW02HL6Jr1LQQ==",
"version": "file:../nocodb-sdk",
"requires": {
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"axios": "^0.21.1",
"jsep": "^1.3.6"
},
"dependencies": {
"axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"requires": {
"follow-redirects": "^1.14.0"
}
}
"cspell": "^4.1.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"jsep": "^1.3.6",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"typescript": "^4.0.2"
}
},
"node-abi": {

2
packages/nc-gui/package.json

@ -49,7 +49,7 @@
"jwt-decode": "^3.1.2",
"locale-codes": "^1.3.1",
"monaco-editor": "^0.33.0",
"nocodb-sdk": "0.99.1",
"nocodb-sdk": "file:../nocodb-sdk",
"papaparse": "^5.3.2",
"socket.io-client": "^4.5.1",
"sortablejs": "^1.15.0",

4
packages/nocodb-sdk/package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "nocodb-sdk",
"version": "0.99.0",
"version": "0.99.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "nocodb-sdk",
"version": "0.99.0",
"version": "0.99.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",

30
packages/nocodb/package-lock.json generated

@ -67,7 +67,7 @@
"nc-lib-gui": "0.99.1",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",
"nocodb-sdk": "0.99.1",
"nocodb-sdk": "file:../nocodb-sdk",
"nodemailer": "^6.4.10",
"object-hash": "^3.0.0",
"os-locale": "^5.0.0",
@ -153,7 +153,6 @@
},
"../nocodb-sdk": {
"version": "0.99.1",
"extraneous": true,
"license": "AGPL-3.0-or-later",
"dependencies": {
"axios": "^0.21.1",
@ -10686,13 +10685,8 @@
"dev": true
},
"node_modules/nocodb-sdk": {
"version": "0.99.1",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.99.1.tgz",
"integrity": "sha512-CfaYgqi4m5jFWaRazsshvLN468Trfq/DxXVAzLz9hIZv88urYbXJaAwbXTNR8q+CTpACabkN9NW02HL6Jr1LQQ==",
"dependencies": {
"axios": "^0.21.1",
"jsep": "^1.3.6"
}
"resolved": "../nocodb-sdk",
"link": true
},
"node_modules/node-abort-controller": {
"version": "3.0.1",
@ -26114,12 +26108,22 @@
"dev": true
},
"nocodb-sdk": {
"version": "0.99.1",
"resolved": "https://registry.npmjs.org/nocodb-sdk/-/nocodb-sdk-0.99.1.tgz",
"integrity": "sha512-CfaYgqi4m5jFWaRazsshvLN468Trfq/DxXVAzLz9hIZv88urYbXJaAwbXTNR8q+CTpACabkN9NW02HL6Jr1LQQ==",
"version": "file:../nocodb-sdk",
"requires": {
"@typescript-eslint/eslint-plugin": "^4.0.1",
"@typescript-eslint/parser": "^4.0.1",
"axios": "^0.21.1",
"jsep": "^1.3.6"
"cspell": "^4.1.0",
"eslint": "^7.8.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^3.0.2",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-prettier": "^4.0.0",
"jsep": "^1.3.6",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.1",
"typescript": "^4.0.2"
}
},
"node-abort-controller": {

2
packages/nocodb/package.json

@ -107,7 +107,7 @@
"nc-lib-gui": "0.99.1",
"nc-plugin": "0.1.2",
"ncp": "^2.0.0",
"nocodb-sdk": "0.99.1",
"nocodb-sdk": "file:../nocodb-sdk",
"nodemailer": "^6.4.10",
"object-hash": "^3.0.0",
"os-locale": "^5.0.0",

80
packages/nocodb/src/lib/db/sql-client/lib/mssql/MssqlClient.ts

@ -356,12 +356,14 @@ class MssqlClient extends KnexClient {
try {
/** ************** START : create _evolution table if not exists *************** */
const exists = await this.sqlClient.schema.withSchema(this.schema).hasTable(args.tn);
const exists = await this.sqlClient.schema
.withSchema(this.schema)
.hasTable(args.tn);
if (!exists) {
await this.sqlClient.schema.withSchema(this.schema).createTable(
args.tn,
function (table) {
await this.sqlClient.schema
.withSchema(this.schema)
.createTable(args.tn, function (table) {
table.increments();
table.string('title').notNullable();
table.string('titleDown').nullable();
@ -371,8 +373,7 @@ class MssqlClient extends KnexClient {
table.integer('status').nullable();
table.dateTime('created');
table.timestamps();
}
);
});
log.debug('Table created:', `${this.getTnPath(args.tn)}`);
} else {
log.debug(`${this.getTnPath(args.tn)} tables exists`);
@ -394,7 +395,9 @@ class MssqlClient extends KnexClient {
log.api(`${_func}:args:`, args);
try {
result.data.value = await this.sqlClient.schema.withSchema(this.schema).hasTable(args.tn);
result.data.value = await this.sqlClient.schema
.withSchema(this.schema)
.hasTable(args.tn);
} catch (e) {
log.ppe(e, _func);
throw e;
@ -1343,7 +1346,8 @@ class MssqlClient extends KnexClient {
log.api(`${func}:args:`, args);
// `DROP TRIGGER ${args.trigger_name}`
try {
const query = `${this.querySeparator()}DROP TRIGGER IF EXISTS ${args.trigger_name
const query = `${this.querySeparator()}DROP TRIGGER IF EXISTS ${
args.trigger_name
}`;
await this.sqlClient.raw(query);
@ -1474,7 +1478,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP FUNCTION IF EXISTS ${args.function_name
`DROP FUNCTION IF EXISTS ${
args.function_name
};${this.querySeparator()}\n${args.create_function}`,
},
],
@ -1482,7 +1487,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP FUNCTION IF EXISTS ${args.function_name
`DROP FUNCTION IF EXISTS ${
args.function_name
};${this.querySeparator()} ${args.oldCreateFunction}`,
},
],
@ -1554,7 +1560,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP PROCEDURE IF EXISTS ${args.procedure_name
`DROP PROCEDURE IF EXISTS ${
args.procedure_name
};${this.querySeparator()}\n${args.create_procedure}`,
},
],
@ -1562,7 +1569,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP PROCEDURE IF EXISTS ${args.procedure_name
`DROP PROCEDURE IF EXISTS ${
args.procedure_name
};${this.querySeparator()}${args.oldCreateProcedure}`,
},
],
@ -1627,7 +1635,8 @@ class MssqlClient extends KnexClient {
try {
// await this.sqlClient.raw(`DROP TRIGGER ${args.trigger_name}`);
await this.sqlClient.raw(
`ALTER TRIGGER ${args.trigger_name} ON ${this.getTnPath(args.tn)} \n${args.timing
`ALTER TRIGGER ${args.trigger_name} ON ${this.getTnPath(args.tn)} \n${
args.timing
} ${args.event}\n AS\n${args.statement}`
);
@ -1712,7 +1721,8 @@ class MssqlClient extends KnexClient {
{
sql:
this.querySeparator() +
`DROP VIEW ${args.view_name} ; ${this.querySeparator()}${args.oldViewDefination
`DROP VIEW ${args.view_name} ; ${this.querySeparator()}${
args.oldViewDefination
};`,
},
],
@ -1807,7 +1817,10 @@ class MssqlClient extends KnexClient {
const downStatement =
this.querySeparator() +
this.sqlClient.schema.withSchema(this.schema).dropTable(args.table).toString();
this.sqlClient.schema
.withSchema(this.schema)
.dropTable(args.table)
.toString();
this.emit(`Success : ${upQuery}`);
@ -1849,7 +1862,15 @@ class MssqlClient extends KnexClient {
BEGIN
SET NOCOUNT ON;
UPDATE ?? Set ?? = GetDate() WHERE ?? in (SELECT ?? FROM Inserted)
END;`, [triggerName, this.getTnPath(args.table_name), this.getTnPath(args.table_name), column.column_name, pk.column_name, pk.column_name]
END;`,
[
triggerName,
this.getTnPath(args.table_name),
this.getTnPath(args.table_name),
column.column_name,
pk.column_name,
pk.column_name,
]
);
upQuery += triggerCreateQuery;
@ -2051,7 +2072,10 @@ class MssqlClient extends KnexClient {
/** ************** create up & down statements *************** */
const upStatement =
this.querySeparator() +
this.sqlClient.schema.withSchema(this.schema).dropTable(args.tn).toString();
this.sqlClient.schema
.withSchema(this.schema)
.dropTable(args.tn)
.toString();
let downQuery = this.querySeparator() + this.createTable(args.tn, args);
this.emit(`Success : ${upStatement}`);
@ -2065,8 +2089,9 @@ class MssqlClient extends KnexClient {
for (const relation of relationsList) {
downQuery +=
this.querySeparator() +
(await this.sqlClient.withSchema(this.schema).schema
.table(relation.tn, (table) => {
(await this.sqlClient
.withSchema(this.schema)
.schema.table(relation.tn, (table) => {
table = table
.foreign(relation.cn, null)
.references(relation.rcn)
@ -2109,7 +2134,8 @@ class MssqlClient extends KnexClient {
)) {
downQuery +=
this.querySeparator() +
this.sqlClient.schema.withSchema(this.schema)
this.sqlClient.schema
.withSchema(this.schema)
.table(tn, function (table) {
if (non_unique) {
table.index(columns, key_name);
@ -2154,7 +2180,9 @@ class MssqlClient extends KnexClient {
try {
const self = this;
await this.sqlClient.schema.table(this.getTnPath(args.childTable), function (table) {
await this.sqlClient.schema.table(
this.getTnPath(args.childTable),
function (table) {
table = table
.foreign(args.childColumn, foreignKeyName)
.references(args.parentColumn)
@ -2166,7 +2194,8 @@ class MssqlClient extends KnexClient {
if (args.onDelete) {
table = table.onDelete(args.onDelete);
}
});
}
);
const upStatement =
this.querySeparator() +
@ -2227,9 +2256,12 @@ class MssqlClient extends KnexClient {
try {
const self = this;
await this.sqlClient.schema.table(this.getTnPath(args.childTable), function (table) {
await this.sqlClient.schema.table(
this.getTnPath(args.childTable),
function (table) {
table.dropForeign(args.childColumn, foreignKeyName);
});
}
);
const upStatement =
this.querySeparator() +

37
packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts

@ -158,9 +158,10 @@ class BaseModelSqlv2 {
qb.orderBy(this.model.primaryKey.column_name);
}
const data = await qb.first();
let data = await qb.first();
if (data) {
data = this.convertAttachmentType(data);
const proto = await this.getProto();
data.__proto__ = proto;
}
@ -365,8 +366,7 @@ class BaseModelSqlv2 {
qb.groupBy(args.column_name);
if (sorts) await sortV2(sorts, qb, this.dbDriver);
applyPaginate(qb, rest);
const data = await qb;
return data;
return this.convertAttachmentType(await qb);
}
async multipleHmList({ colId, ids }, args: { limit?; offset? } = {}) {
@ -672,10 +672,10 @@ class BaseModelSqlv2 {
);
let children = await this.extractRawQueryAndExec(finalQb);
children = this.convertAttachmentType(children);
if (this.isMySQL) {
children = children[0];
}
children = this.convertAttachmentType(children);
const proto = await (
await Model.getBaseModelSQL({
id: rtnId,
@ -967,8 +967,8 @@ class BaseModelSqlv2 {
applyPaginate(qb, rest);
const proto = await childModel.getProto();
const data = await qb;
let data = await qb;
data = this.convertAttachmentType(data);
return data.map((c) => {
c.__proto__ = proto;
return c;
@ -2737,6 +2737,15 @@ class BaseModelSqlv2 {
: await this.dbDriver.raw(query);
}
private _convertAttachmentType(attachmentColumns, d) {
attachmentColumns.forEach((col) => {
if (d[col.title] && typeof d[col.title] === 'string') {
d[col.title] = JSON.parse(d[col.title]);
}
});
return d;
}
private convertAttachmentType(data) {
// attachment is stored in text and parse in UI
// convertAttachmentType is used to convert the response in string to array of object in API response
@ -2745,17 +2754,13 @@ class BaseModelSqlv2 {
(c) => c.uidt === UITypes.Attachment
);
if (attachmentColumns.length) {
if (!Array.isArray(data)) {
data = [data];
}
data = data.map((d) => {
attachmentColumns.forEach((col) => {
if (d[col.title] && typeof d[col.title] === 'string') {
d[col.title] = JSON.parse(d[col.title]);
if (Array.isArray(data)) {
data = data.map((d) =>
this._convertAttachmentType(attachmentColumns, d)
);
} else {
this._convertAttachmentType(attachmentColumns, data);
}
});
return d;
});
}
}
return data;

9
packages/nocodb/src/lib/meta/helpers/populateSamplePayload.ts

@ -94,7 +94,14 @@ async function getSampleColumnValue(column: Column): Promise<any> {
break;
case UITypes.Attachment:
{
return '[{"url":"https://nocodb.com/dummy.png","title":"image.png","mimetype":"image/png","size":0}]';
return [
{
url: 'https://nocodb.com/dummy.png',
title: 'image.png',
mimetype: 'image/png',
size: 0,
},
];
}
break;
case UITypes.Checkbox:

Loading…
Cancel
Save