fix (dashboard): Parse foreign key relations correctly (#3458)
### **PR Type**
Bug fix, Tests
___
### **Description**
- Pass updated table name to refetch queries
- Extend `onSubmit` callbacks with `tableName`
- Strip quotes from foreign key column names
- Add and restructure tests for extractor
___
<details> <summary><h3> File Walkthrough</h3></summary>
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Bug
fix</strong></td><td><table>
<tr>
<td>
<details>
<summary><strong>DataBrowserSidebar.tsx</strong><dd><code>Update
refetchQueries with new tableName</code>
</dd></summary>
<hr>
dashboard/src/features/orgs/projects/database/dataGrid/components/DataBrowserSidebar/DataBrowserSidebar.tsx
<ul><li>Accept <code>tableName</code> in <code>onSubmit</code>
callback<br> <li> Use <code>tableName</code> for
<code>refetchQueries</code> key</ul>
</details>
</td>
<td><a
href="https://github.com/nhost/nhost/pull/3458/files#diff-3a9ff7af4a31fbf7e501a77399b2b35306d9e635b021c93f1bc13fc4e225219c">+2/-2</a>
</td>
</tr>
<tr>
<td>
<details>
<summary><strong>EditTableForm.tsx</strong><dd><code>Extend onSubmit to
receive tableName</code>
</dd></summary>
<hr>
dashboard/src/features/orgs/projects/database/dataGrid/components/EditTableForm/EditTableForm.tsx
<ul><li>Change <code>onSubmit</code> prop to accept
<code>tableName</code><br> <li> Pass <code>updatedTable.name</code> to
<code>onSubmit</code></ul>
</details>
</td>
<td><a
href="https://github.com/nhost/nhost/pull/3458/files#diff-e628e74884ed048e1498960b80ad4d2a9fa6b4e05c89545c404e0ed50b43e50a">+2/-2</a>
</td>
</tr>
<tr>
<td>
<details>
<summary><strong>extractForeignKeyRelation.ts</strong><dd><code>Strip
quotes from extracted column names</code>
</dd></summary>
<hr>
dashboard/src/features/orgs/projects/database/dataGrid/utils/extractForeignKeyRelation/extractForeignKeyRelation.ts
- Remove surrounding parentheses and double quotes from `columnName`
</details>
</td>
<td><a
href="https://github.com/nhost/nhost/pull/3458/files#diff-ff940d21b4207265ccae2acaa2e5b8d2a0edc01fd51f14d1c0f6beed43596c49">+1/-1</a>
</td>
</tr>
</table></td></tr><tr><td><strong>Tests</strong></td><td><table>
<tr>
<td>
<details>
<summary><strong>extractForeignKeyRelation.test.ts</strong><dd><code>Restructure
extractor tests and add cases</code>
</dd></summary>
<hr>
dashboard/src/features/orgs/projects/database/dataGrid/utils/extractForeignKeyRelation/extractForeignKeyRelation.test.ts
<ul><li>Group tests under <code>describe</code> blocks<br> <li> Add test
for capital-letter column names<br> <li> Consolidate no-action
scenarios</ul>
</details>
</td>
<td><a
href="https://github.com/nhost/nhost/pull/3458/files#diff-9f5bd2c96f0cdcb925343201e389d2d57d8f1fb2adf7daf522338939c613f426">+126/-109</a></td>
</tr>
</table></td></tr><tr><td><strong>Configuration
changes</strong></td><td><table>
<tr>
<td>
<details>
<summary><strong>cold-toys-bow.md</strong><dd><code>Add changelog for
foreign key fix</code>
</dd></summary>
<hr>
.changeset/cold-toys-bow.md
- Add dashboard patch changelog entry
</details>
</td>
<td><a
href="https://github.com/nhost/nhost/pull/3458/files#diff-fe0a7d0ca4fb132b472a16dacc0ebc4ade0b69f3280d10ac4c9b35c955ccdce3">+5/-0</a>
</td>
</tr>
</table></td></tr></tr></tbody></table>
</details>
___
This commit is contained in:
5
.changeset/cold-toys-bow.md
Normal file
5
.changeset/cold-toys-bow.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@nhost/dashboard': patch
|
||||
---
|
||||
|
||||
fix (dashboard): Parse foreign key relations correctly
|
||||
@@ -408,9 +408,9 @@ function DataBrowserSidebarContent({
|
||||
title: 'Edit Table',
|
||||
component: (
|
||||
<EditTableForm
|
||||
onSubmit={async () => {
|
||||
onSubmit={async (tableName) => {
|
||||
await queryClient.refetchQueries([
|
||||
`${dataSourceSlug}.${table.table_schema}.${table.table_name}`,
|
||||
`${dataSourceSlug}.${table.table_schema}.${tableName}`,
|
||||
]);
|
||||
await refetch();
|
||||
}}
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface EditTableFormProps
|
||||
/**
|
||||
* Function to be called when the form is submitted.
|
||||
*/
|
||||
onSubmit?: () => Promise<void>;
|
||||
onSubmit?: (tableName: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export default function EditTableForm({
|
||||
@@ -182,7 +182,7 @@ export default function EditTableForm({
|
||||
}
|
||||
|
||||
if (onSubmit) {
|
||||
await onSubmit();
|
||||
await onSubmit(updatedTable.name);
|
||||
}
|
||||
|
||||
if (originalTable.table_name !== updatedTable.name) {
|
||||
|
||||
@@ -1,122 +1,139 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import extractForeignKeyRelation from './extractForeignKeyRelation';
|
||||
|
||||
test('should return null if there is no match', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'something_that_is_not_a_foreign_key',
|
||||
),
|
||||
).toBe(null);
|
||||
});
|
||||
|
||||
test('should extract data from a raw foreign key constraint', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE RESTRICT ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: 'auth',
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'RESTRICT',
|
||||
describe('extractForeignKeyRelation', () => {
|
||||
test('should return null if there is no match', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'something_that_is_not_a_foreign_key',
|
||||
),
|
||||
).toBe(null);
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: 'auth',
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'CASCADE',
|
||||
deleteAction: 'CASCADE',
|
||||
test('should extract data from a raw foreign key constraint', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE RESTRICT ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: 'auth',
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'RESTRICT',
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE CASCADE ON DELETE CASCADE',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: 'auth',
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'CASCADE',
|
||||
deleteAction: 'CASCADE',
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE SET DEFAULT ON DELETE SET NULL',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: 'auth',
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'SET DEFAULT',
|
||||
deleteAction: 'SET NULL',
|
||||
});
|
||||
});
|
||||
test("should return column's name with a capital letter without quotes", () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY ("userId") REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'userId',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'RESTRICT',
|
||||
});
|
||||
});
|
||||
test('should return null as referenced schema if it is not present', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'RESTRICT',
|
||||
});
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES auth.users(id) ON UPDATE SET DEFAULT ON DELETE SET NULL',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: 'auth',
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'SET DEFAULT',
|
||||
deleteAction: 'SET NULL',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return null as referenced schema if it is not present', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE RESTRICT ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'RESTRICT',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return NO ACTION for update and delete actions if they are not present', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'NO ACTION',
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'NO ACTION',
|
||||
deleteAction: 'RESTRICT',
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id)',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'NO ACTION',
|
||||
deleteAction: 'NO ACTION',
|
||||
test('should return NO ACTION for update and delete actions if they are not present', () => {
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id) ON UPDATE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'RESTRICT',
|
||||
deleteAction: 'NO ACTION',
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'NO ACTION',
|
||||
deleteAction: 'RESTRICT',
|
||||
});
|
||||
|
||||
expect(
|
||||
extractForeignKeyRelation(
|
||||
'table_id_fkey',
|
||||
'FOREIGN KEY (user_id) REFERENCES users(id)',
|
||||
),
|
||||
).toMatchObject({
|
||||
name: 'table_id_fkey',
|
||||
columnName: 'user_id',
|
||||
referencedSchema: null,
|
||||
referencedTable: 'users',
|
||||
referencedColumn: 'id',
|
||||
updateAction: 'NO ACTION',
|
||||
deleteAction: 'NO ACTION',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function extractForeignKeyRelation(
|
||||
|
||||
return {
|
||||
name,
|
||||
columnName: columnName.replace(/(^\(|\)$)/gi, ''),
|
||||
columnName: columnName.replace(/(^\(|\)$)/gi, '').replaceAll('"', ''),
|
||||
referencedSchema,
|
||||
referencedTable,
|
||||
referencedColumn: referencedColumn.replace(/(^\(|\)$)/gi, ''),
|
||||
|
||||
Reference in New Issue
Block a user