mirror of
https://github.com/docmost/docmost.git
synced 2026-08-29 10:05:03 +08:00
fix(bases): strip all property-id refs from view config on delete
This commit is contained in:
@@ -74,4 +74,53 @@ describe('stripPropertyFromViewConfig', () => {
|
|||||||
sorts: [{ propertyId: 'p-keep', direction: 'asc' as const }],
|
sorts: [{ propertyId: 'p-keep', direction: 'asc' as const }],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('strips visiblePropertyIds entries pointing at the deleted property', () => {
|
||||||
|
const config = {
|
||||||
|
visiblePropertyIds: ['p-deleted', 'p-keep'],
|
||||||
|
};
|
||||||
|
expect(stripPropertyFromViewConfig(config, 'p-deleted')).toEqual({
|
||||||
|
visiblePropertyIds: ['p-keep'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('strips hiddenPropertyIds entries pointing at the deleted property', () => {
|
||||||
|
const config = {
|
||||||
|
hiddenPropertyIds: ['p-deleted', 'p-keep'],
|
||||||
|
};
|
||||||
|
expect(stripPropertyFromViewConfig(config, 'p-deleted')).toEqual({
|
||||||
|
hiddenPropertyIds: ['p-keep'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('strips propertyOrder entries pointing at the deleted property', () => {
|
||||||
|
const config = {
|
||||||
|
propertyOrder: ['p-other', 'p-deleted', 'p-keep'],
|
||||||
|
};
|
||||||
|
expect(stripPropertyFromViewConfig(config, 'p-deleted')).toEqual({
|
||||||
|
propertyOrder: ['p-other', 'p-keep'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes propertyWidths entry for the deleted property', () => {
|
||||||
|
const config = {
|
||||||
|
propertyWidths: { 'p-deleted': 120, 'p-keep': 200 },
|
||||||
|
};
|
||||||
|
expect(stripPropertyFromViewConfig(config, 'p-deleted')).toEqual({
|
||||||
|
propertyWidths: { 'p-keep': 200 },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removes the visiblePropertyIds/propertyWidths keys when they become empty', () => {
|
||||||
|
const config = {
|
||||||
|
visiblePropertyIds: ['p-deleted'],
|
||||||
|
hiddenPropertyIds: ['p-deleted'],
|
||||||
|
propertyOrder: ['p-deleted'],
|
||||||
|
propertyWidths: { 'p-deleted': 120 },
|
||||||
|
sorts: [{ propertyId: 'p-keep', direction: 'asc' as const }],
|
||||||
|
};
|
||||||
|
expect(stripPropertyFromViewConfig(config, 'p-deleted')).toEqual({
|
||||||
|
sorts: [{ propertyId: 'p-keep', direction: 'asc' as const }],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
parseTypeOptions,
|
parseTypeOptions,
|
||||||
validateTypeOptions,
|
validateTypeOptions,
|
||||||
isSystemPropertyType,
|
isSystemPropertyType,
|
||||||
|
ViewConfig,
|
||||||
} from '../base.schemas';
|
} from '../base.schemas';
|
||||||
import { generateJitteredKeyBetween } from 'fractional-indexing-jittered';
|
import { generateJitteredKeyBetween } from 'fractional-indexing-jittered';
|
||||||
import { QueueJob, QueueName } from '../../../integrations/queue/constants';
|
import { QueueJob, QueueName } from '../../../integrations/queue/constants';
|
||||||
@@ -534,21 +535,17 @@ export class BasePropertyService {
|
|||||||
trx,
|
trx,
|
||||||
});
|
});
|
||||||
for (const view of views) {
|
for (const view of views) {
|
||||||
const before = (view.config ?? {}) as Record<string, unknown>;
|
|
||||||
const next = stripPropertyFromViewConfig(
|
const next = stripPropertyFromViewConfig(
|
||||||
view.config as any,
|
view.config as ViewConfig,
|
||||||
dto.propertyId,
|
dto.propertyId,
|
||||||
);
|
);
|
||||||
const after = next as Record<string, unknown>;
|
if (JSON.stringify(view.config ?? {}) === JSON.stringify(next)) {
|
||||||
if (
|
|
||||||
Object.keys(before).length === Object.keys(after).length &&
|
|
||||||
Object.keys(before).every((k) => k in after) &&
|
|
||||||
JSON.stringify(before) === JSON.stringify(after)
|
|
||||||
) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await this.baseViewRepo.updateView(
|
await this.baseViewRepo.updateView(
|
||||||
view.id,
|
view.id,
|
||||||
|
// `config` column is typed `Json` by Kysely; ViewConfig is a Zod
|
||||||
|
// inferred shape that isn't structurally assignable to `Json`.
|
||||||
{ config: next as any },
|
{ config: next as any },
|
||||||
{ workspaceId, trx },
|
{ workspaceId, trx },
|
||||||
);
|
);
|
||||||
@@ -582,6 +579,11 @@ export class BasePropertyService {
|
|||||||
`Enqueue of cell-gc failed for property ${dto.propertyId}; reverting soft-delete`,
|
`Enqueue of cell-gc failed for property ${dto.propertyId}; reverting soft-delete`,
|
||||||
err as Error,
|
err as Error,
|
||||||
);
|
);
|
||||||
|
// Best-effort revert: restores `deletedAt: null` on the property. The
|
||||||
|
// view-config cleanup and schema-bump that ran inside the earlier
|
||||||
|
// `executeTx` are NOT reverted — restoring those would require capturing
|
||||||
|
// the original configs before the transaction. Rare path (queue down);
|
||||||
|
// acceptable today.
|
||||||
try {
|
try {
|
||||||
await this.basePropertyRepo.updateProperty(dto.propertyId, {
|
await this.basePropertyRepo.updateProperty(dto.propertyId, {
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
|
|||||||
@@ -53,5 +53,29 @@ export function stripPropertyFromViewConfig(
|
|||||||
delete next.choiceOrder;
|
delete next.choiceOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.visiblePropertyIds) {
|
||||||
|
const kept = config.visiblePropertyIds.filter((id) => id !== propertyId);
|
||||||
|
if (kept.length > 0) next.visiblePropertyIds = kept;
|
||||||
|
else delete next.visiblePropertyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.hiddenPropertyIds) {
|
||||||
|
const kept = config.hiddenPropertyIds.filter((id) => id !== propertyId);
|
||||||
|
if (kept.length > 0) next.hiddenPropertyIds = kept;
|
||||||
|
else delete next.hiddenPropertyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.propertyOrder) {
|
||||||
|
const kept = config.propertyOrder.filter((id) => id !== propertyId);
|
||||||
|
if (kept.length > 0) next.propertyOrder = kept;
|
||||||
|
else delete next.propertyOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.propertyWidths && propertyId in config.propertyWidths) {
|
||||||
|
const { [propertyId]: _removed, ...rest } = config.propertyWidths;
|
||||||
|
if (Object.keys(rest).length > 0) next.propertyWidths = rest;
|
||||||
|
else delete next.propertyWidths;
|
||||||
|
}
|
||||||
|
|
||||||
return next as ViewConfig;
|
return next as ViewConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user