Browse Source

Merge pull request #5936 from nocodb/fix/5934-delete-table

fix: Skip relation delete if it's a virtual relation
pull/5940/head
Raju Udava 1 year ago committed by GitHub
parent
commit
4331041827
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui/components/cell/MultiSelect.vue
  2. 6
      packages/nc-gui/components/cell/SingleSelect.vue
  3. 2
      packages/nc-gui/components/dashboard/TreeView.vue
  4. 11
      packages/nc-gui/components/smartsheet/Form.vue
  5. 6
      packages/nc-gui/components/smartsheet/Grid.vue
  6. 8
      packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
  7. 2
      packages/nc-gui/composables/useColumnCreateStore.ts
  8. 3
      packages/nc-gui/composables/useMetas.ts
  9. 10
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue
  10. 12
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue
  11. 27
      packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts
  12. 2
      packages/nocodb/src/meta/meta.service.ts
  13. 8
      packages/nocodb/src/models/Model.ts
  14. 14
      packages/nocodb/src/models/View.ts
  15. 10
      packages/nocodb/src/services/columns.service.ts
  16. 5
      packages/nocodb/src/services/tables.service.ts
  17. 5
      packages/nocodb/src/services/users/users.service.ts

4
packages/nc-gui/components/cell/MultiSelect.vue

@ -8,6 +8,7 @@ import {
ActiveCellInj,
CellClickHookInj,
ColumnInj,
EditModeInj,
IsKanbanInj,
ReadonlyInj,
RowHeightInj,
@ -27,7 +28,6 @@ import {
useRoles,
useSelectedCellKeyupListener,
watch,
EditModeInj
} from '#imports'
import MdiCloseCircle from '~icons/mdi/close-circle'
@ -101,7 +101,7 @@ const isOptionMissing = computed(() => {
const hasEditRoles = computed(() => hasRole('owner', true) || hasRole('creator', true) || hasRole('editor', true))
const editAllowed = computed(() => (hasEditRoles.value || isForm.value) && (active.value))
const editAllowed = computed(() => (hasEditRoles.value || isForm.value) && active.value)
const vModel = computed({
get: () => {

6
packages/nc-gui/components/cell/SingleSelect.vue

@ -8,6 +8,7 @@ import {
ActiveCellInj,
CellClickHookInj,
ColumnInj,
EditModeInj,
IsFormInj,
IsKanbanInj,
ReadonlyInj,
@ -23,7 +24,6 @@ import {
useRoles,
useSelectedCellKeyupListener,
watch,
EditModeInj
} from '#imports'
interface Props {
@ -96,7 +96,7 @@ const isOptionMissing = computed(() => {
const hasEditRoles = computed(() => hasRole('owner', true) || hasRole('creator', true) || hasRole('editor', true))
const editAllowed = computed(() => (hasEditRoles.value || isForm.value) && (active.value))
const editAllowed = computed(() => (hasEditRoles.value || isForm.value) && active.value)
const vModel = computed({
get: () => tempSelectedOptState.value ?? modelValue,
@ -257,7 +257,7 @@ const selectedOpt = computed(() => {
<template>
<div class="h-full w-full flex items-center nc-single-select" :class="{ 'read-only': readOnly }" @click="toggleMenu">
<div v-if="!(active || isEditable) ">
<div v-if="!(active || isEditable)">
<a-tag v-if="selectedOpt" class="rounded-tag" :color="selectedOpt.color">
<span
:style="{

2
packages/nc-gui/components/dashboard/TreeView.vue

@ -97,7 +97,7 @@ const initSortable = (el: Element) => {
onEnd: async (evt) => {
const { newIndex = 0, oldIndex = 0 } = evt
if(newIndex === oldIndex) return
if (newIndex === oldIndex) return
const itemEl = evt.item as HTMLLIElement
const item = tablesById[itemEl.dataset.id as string]

11
packages/nc-gui/components/smartsheet/Form.vue

@ -535,7 +535,7 @@ watch(view, (nextView) => {
<a-textarea
v-model:value="formViewData.heading"
class="w-full !font-bold !text-4xl !border-0 !border-b-1 !border-dashed !rounded-none !border-gray-400"
:style="{ borderRightWidth: '0px !important', 'height': '54px', 'min-height': '54px', resize: 'vertical' }"
:style="{ 'borderRightWidth': '0px !important', 'height': '54px', 'min-height': '54px', 'resize': 'vertical' }"
size="large"
hide-details
placeholder="Form Title"
@ -554,7 +554,7 @@ watch(view, (nextView) => {
<a-textarea
v-model:value="formViewData.subheading"
class="w-full !border-0 !border-b-1 !border-dashed !rounded-none !border-gray-400"
:style="{ borderRightWidth: '0px !important', height: '40px', 'min-height': '40px', resize: 'vertical' }"
:style="{ 'borderRightWidth': '0px !important', 'height': '40px', 'min-height': '40px', 'resize': 'vertical' }"
size="large"
hide-details
:placeholder="$t('msg.info.formDesc')"
@ -743,7 +743,9 @@ watch(view, (nextView) => {
</LazySmartsheetDivDataCell>
</a-form-item>
<div class="nc-form-help-text text-gray-500 text-xs" data-testid="nc-form-input-help-text-label">{{ element.description }}</div>
<div class="nc-form-help-text text-gray-500 text-xs" data-testid="nc-form-input-help-text-label">
{{ element.description }}
</div>
</div>
</template>
@ -861,7 +863,8 @@ watch(view, (nextView) => {
}
}
.nc-form-help-text, .nc-input-required-error {
.nc-form-help-text,
.nc-input-required-error {
max-width: 100%;
word-break: break-all;
white-space: pre-line;

6
packages/nc-gui/components/smartsheet/Grid.vue

@ -746,7 +746,7 @@ const saveOrUpdateRecords = async (args: { metaValue?: TableType; viewMetaValue?
async function reloadViewDataHandler(shouldShowLoading: boolean | void) {
// save any unsaved data before reload
await saveOrUpdateRecords();
await saveOrUpdateRecords()
// set value if spinner should be hidden
showLoading.value = !!shouldShowLoading
@ -768,7 +768,7 @@ onBeforeUnmount(async () => {
const viewMetaValue = view.value
const dataValue = data.value
if (viewMetaValue) {
getMeta(viewMetaValue.fk_model_id).then((res) => {
getMeta(viewMetaValue.fk_model_id, false, true).then((res) => {
const metaValue = res
if (!metaValue) return
saveOrUpdateRecords({
@ -815,7 +815,7 @@ watch(
switchingTab.value = true
// whenever tab changes or view changes save any unsaved data
if (old?.id) {
const oldMeta = await getMeta(old.fk_model_id!)
const oldMeta = await getMeta(old.fk_model_id!, false, true)
if (oldMeta) {
await saveOrUpdateRecords({
viewMetaValue: old,

8
packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue

@ -129,11 +129,9 @@ const filterOption = (value: string, option: { key: string }) => option.key.toLo
<div class="flex flex-row">
<a-form-item>
<a-checkbox v-model:checked="vModel.virtual" :disabled="appInfo.isCloud" name="virtual"
@change="onDataTypeChange"
>Virtual Relation
</a-checkbox
>
<a-checkbox v-model:checked="vModel.virtual" :disabled="appInfo.isCloud" name="virtual" @change="onDataTypeChange"
>Virtual Relation
</a-checkbox>
</a-form-item>
</div>
</div>

2
packages/nc-gui/composables/useColumnCreateStore.ts

@ -291,7 +291,7 @@ const [useProvideColumnCreateStore, useColumnCreateStore] = createInjectionState
isMssql,
isPg,
isMysql,
isXcdbBase
isXcdbBase,
}
},
)

3
packages/nc-gui/composables/useMetas.ts

@ -26,7 +26,7 @@ export function useMetas() {
}
// todo: this needs a proper refactor, arbitrary waiting times are usually not a good idea
const getMeta = async (tableIdOrTitle: string, force = false): Promise<TableType | null> => {
const getMeta = async (tableIdOrTitle: string, force = false, skipIfCacheMiss = false): Promise<TableType | null> => {
if (!tableIdOrTitle) return null
/** wait until loading is finished if requesting same meta */
if (!force && loadingState.value[tableIdOrTitle]) {
@ -58,6 +58,7 @@ export function useMetas() {
return metas.value[tableIdOrTitle]
}
}
if (skipIfCacheMiss) return null
loadingState.value[tableIdOrTitle] = true

10
packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue

@ -75,10 +75,7 @@ const onDecode = async (scannedCodeValue: string) => {
class="color-transition relative flex flex-col justify-center gap-2 w-full max-w-[max(33%,600px)] m-auto py-4 pb-8 px-16 md:(bg-white dark:bg-slate-700 rounded-lg border-1 border-gray-200 shadow-xl)"
>
<template v-if="sharedFormView">
<h1
class="prose-2xl font-bold self-center my-4"
style="word-break: break-all"
>
<h1 class="prose-2xl font-bold self-center my-4" style="word-break: break-all">
{{ sharedFormView.heading }}
</h1>
@ -189,7 +186,10 @@ const onDecode = async (scannedCodeValue: string) => {
</a-button>
</LazySmartsheetDivDataCell>
<div class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1" style="word-break: break-all">
<div
class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1"
style="word-break: break-all"
>
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500">
{{ error.$message }}
</div>

12
packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

@ -244,10 +244,7 @@ onMounted(() => {
class="max-w-[max(33%,600px)] mx-auto flex flex-col justify-end"
>
<div class="px-4 md:px-0 flex flex-col justify-end">
<h1
class="prose-2xl font-bold self-center my-4"
data-testid="nc-survey-form__heading"
style="word-break: break-all">
<h1 class="prose-2xl font-bold self-center my-4" data-testid="nc-survey-form__heading" style="word-break: break-all">
{{ sharedFormView.heading }}
</h1>
@ -309,7 +306,10 @@ onMounted(() => {
@update:edit-enabled="editEnabled[index] = $event"
/>
<div class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1" style="word-break: break-all">
<div
class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1"
style="word-break: break-all"
>
<div v-for="error of v$.localState[field.title]?.$errors" :key="error" class="text-red-500">
{{ error.$message }}
</div>
@ -353,7 +353,7 @@ onMounted(() => {
:mouse-enter-delay="0.25"
:mouse-leave-delay="0"
>
<!-- Ok button for question -->
<!-- Ok button for question -->
<button
class="bg-opacity-100 scaling-btn flex items-center gap-1"
data-testid="nc-survey-form__btn-next"

27
packages/nocodb/src/db/sql-mgr/v2/ProjectMgrv2.ts

@ -1,21 +1,24 @@
import { MetaService } from '../../../meta/meta.service'
import SqlMgrv2 from './SqlMgrv2'
import SqlMgrv2Trans from './SqlMgrv2Trans'
import SqlMgrv2 from './SqlMgrv2';
import SqlMgrv2Trans from './SqlMgrv2Trans';
import type { MetaService } from '../../../meta/meta.service';
// import type NcMetaIO from '../../../meta/NcMetaIO';
import type Base from '../../../models/Base'
import type Base from '../../../models/Base';
export default class ProjectMgrv2 {
private static sqlMgrMap: {
[key: string]: SqlMgrv2;
} = {}
} = {};
public static getSqlMgr(project: { id: string }, ncMeta: MetaService = null): SqlMgrv2 {
if (ncMeta) return new SqlMgrv2(project, ncMeta)
public static getSqlMgr(
project: { id: string },
ncMeta: MetaService = null,
): SqlMgrv2 {
if (ncMeta) return new SqlMgrv2(project, ncMeta);
if (!this.sqlMgrMap[project.id]) {
this.sqlMgrMap[project.id] = new SqlMgrv2(project)
this.sqlMgrMap[project.id] = new SqlMgrv2(project);
}
return this.sqlMgrMap[project.id]
return this.sqlMgrMap[project.id];
}
public static async getSqlMgrTrans(
@ -24,8 +27,8 @@ export default class ProjectMgrv2 {
ncMeta: any,
base: Base,
): Promise<SqlMgrv2Trans> {
const sqlMgr = new SqlMgrv2Trans(project, ncMeta, base)
await sqlMgr.startTransaction(base)
return sqlMgr
const sqlMgr = new SqlMgrv2Trans(project, ncMeta, base);
await sqlMgr.startTransaction(base);
return sqlMgr;
}
}

2
packages/nocodb/src/meta/meta.service.ts

@ -619,7 +619,7 @@ export class MetaService {
offset?: number;
xcCondition?;
fields?: string[];
orderBy: { [key: string]: 'asc' | 'desc' };
orderBy?: { [key: string]: 'asc' | 'desc' };
},
): Promise<any[]> {
const query = this.knexConnection(target);

8
packages/nocodb/src/models/Model.ts

@ -293,9 +293,9 @@ export default class Model implements TableType {
));
if (!modelData) {
modelData = await ncMeta.metaGet2(null, null, MetaTable.MODELS, k);
modelData.meta = parseMetaProp(modelData);
}
if (modelData) {
modelData.meta = parseMetaProp(modelData);
await NocoCache.set(`${CacheScope.MODEL}:${modelData.id}`, modelData);
return new Model(modelData);
}
@ -327,8 +327,10 @@ export default class Model implements TableType {
table_name,
},
);
modelData.meta = parseMetaProp(modelData);
await NocoCache.set(`${CacheScope.MODEL}:${modelData.id}`, modelData);
if (modelData) {
modelData.meta = parseMetaProp(modelData);
await NocoCache.set(`${CacheScope.MODEL}:${modelData.id}`, modelData);
}
// modelData.filters = await Filter.getFilterObject({
// viewId: modelData.id
// });

14
packages/nocodb/src/models/View.ts

@ -127,8 +127,10 @@ export default class View implements ViewType {
));
if (!view) {
view = await ncMeta.metaGet2(null, null, MetaTable.VIEWS, viewId);
view.meta = parseMetaProp(view);
await NocoCache.set(`${CacheScope.VIEW}:${view.id}`, view);
if (view) {
view.meta = parseMetaProp(view);
await NocoCache.set(`${CacheScope.VIEW}:${view.id}`, view);
}
}
return view && new View(view);
@ -205,8 +207,10 @@ export default class View implements ViewType {
},
null,
);
view.meta = parseMetaProp(view);
await NocoCache.set(`${CacheScope.VIEW}:${fk_model_id}:default`, view);
if (view) {
view.meta = parseMetaProp(view);
await NocoCache.set(`${CacheScope.VIEW}:${fk_model_id}:default`, view);
}
}
return view && new View(view);
}
@ -1273,7 +1277,7 @@ export default class View implements ViewType {
);
}
async delete(ncMeta = Noco.ncMeta){
async delete(ncMeta = Noco.ncMeta) {
await View.delete(this.id, ncMeta);
}

10
packages/nocodb/src/services/columns.service.ts

@ -1216,6 +1216,7 @@ export class ColumnsService {
childColumn: mmParentCol,
base,
ncMeta,
virtual: !!relationColOpt.virtual,
},
true,
);
@ -1230,6 +1231,7 @@ export class ColumnsService {
childColumn: mmChildCol,
base,
ncMeta,
virtual: !!relationColOpt.virtual,
},
true,
);
@ -1387,6 +1389,7 @@ export class ColumnsService {
parentTable,
sqlMgr,
ncMeta = Noco.ncMeta,
virtual,
}: {
relationColOpt: LinkToAnotherRecordColumn;
base: Base;
@ -1396,6 +1399,7 @@ export class ColumnsService {
parentTable: Model;
sqlMgr: SqlMgrv2;
ncMeta?: MetaService;
virtual?: boolean;
},
ignoreFkDelete = false,
) => {
@ -1424,7 +1428,7 @@ export class ColumnsService {
foreignKeyName = relationColOpt.fk_index_name;
}
if (!relationColOpt?.virtual) {
if (!relationColOpt?.virtual && !virtual) {
// todo: handle relation delete exception
try {
await sqlMgr.sqlOpPlus(base, 'relationDelete', {
@ -1435,7 +1439,7 @@ export class ColumnsService {
foreignKeyName,
});
} catch (e) {
console.log(e);
console.log(e.message);
}
}
@ -1481,7 +1485,7 @@ export class ColumnsService {
if (index.cn !== childColumn.column_name) continue;
await sqlMgr.sqlOpPlus(base, 'indexDelete', {
...index,
...index,
tn: cTable.table_name,
columns: [childColumn.column_name],
indexName: index.index_name,

5
packages/nocodb/src/services/tables.service.ts

@ -16,7 +16,6 @@ import getTableNameAlias, { getColumnNameAlias } from '../helpers/getTableName';
import mapDefaultDisplayValue from '../helpers/mapDefaultDisplayValue';
import {
Audit,
Base,
Column,
Model,
ModelRoleVisibility,
@ -247,6 +246,10 @@ export class TablesService {
id: param.tableId,
});
if (!table) {
NcError.notFound('Table not found');
}
// todo: optimise
const viewList = <View[]>(
await this.xcVisibilityMetaGet(table.project_id, [table])

5
packages/nocodb/src/services/users/users.service.ts

@ -51,7 +51,10 @@ export class UsersService {
email: string;
lastname: any;
}) {
return this.metaService.metaInsert2(null, null, MetaTable.USERS, { ...param, email: param.email?.toLowerCase() });
return this.metaService.metaInsert2(null, null, MetaTable.USERS, {
...param,
email: param.email?.toLowerCase(),
});
}
async registerNewUserIfAllowed({

Loading…
Cancel
Save