Skip to content

Commit

Permalink
✨ feat(metadata-columsn): implement metadata columns
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Dec 19, 2023
1 parent ea2449c commit dd79434
Show file tree
Hide file tree
Showing 15 changed files with 359 additions and 200 deletions.
4 changes: 4 additions & 0 deletions src/__tests__/_/api-handlers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ const CONFIG_VALUES = {
primary: `#4b38b3`,
primaryDark: `#111111`,
},
metadata_columns: {
createdAt: `created_at`,
updatedAt: `updated_at`,
},
disabled_entities: ["disabled-entity-1", "disabled-entity-2"],
menu_entities_order: [],
disabled_menu_entities: ["entity-3"],
Expand Down
148 changes: 148 additions & 0 deletions src/__tests__/admin/settings/data.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React from "react";
import { render, screen, waitFor } from "@testing-library/react";
import { ApplicationRoot } from "frontend/components/ApplicationRoot";
import userEvent from "@testing-library/user-event";
import GeneralDataSettings from "pages/admin/settings/data";

import { setupApiHandlers } from "__tests__/_/setupApihandlers";

setupApiHandlers();

describe("pages/admin/settings/data", () => {
beforeAll(() => {
const useRouter = jest.spyOn(require("next/router"), "useRouter");
useRouter.mockImplementation(() => ({
asPath: "/",
}));
});

describe("Metadata", () => {
it("should display metadata columns", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);
await waitFor(() => {
expect(screen.getByLabelText("Created At")).toHaveValue("created_at");
});
expect(screen.getByLabelText("Updated At")).toHaveValue("updated_at");
});

it("should update metadata columns successfully", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);

await userEvent.type(screen.getByLabelText("Created At"), "-created");
await userEvent.type(screen.getByLabelText("Updated At"), "-updated");

await userEvent.click(
screen.getByRole("button", { name: "Save Metadata Columns" })
);

expect(await screen.findByRole("status")).toHaveTextContent(
"Metadata Columns Saved Successfully"
);
});

it("should display updated date values", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);
await waitFor(() => {
expect(screen.getByLabelText("Created At")).toHaveValue(
"created_at-created"
);
});
expect(screen.getByLabelText("Updated At")).toHaveValue(
"updated_at-updated"
);
});
});

describe("Date", () => {
it("should display date values", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);
await waitFor(() => {
expect(screen.getByLabelText("Format")).toHaveValue("do MMM yyyy");
});
});

it("should update date successfully", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);

await userEvent.clear(screen.getByLabelText("Format"));

await userEvent.type(screen.getByLabelText("Format"), "yyyy MMM do");

await userEvent.click(
screen.getByRole("button", { name: "Close Toast" })
);

await userEvent.click(
screen.getByRole("button", { name: "Save Date Format" })
);

expect(await screen.findByRole("status")).toHaveTextContent(
"Date Format Saved Successfully"
);
});

it("should display updated date values", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);
await waitFor(() => {
expect(screen.getByLabelText("Format")).toHaveValue("yyyy MMM do");
});
});

describe("invalid date formats", () => {
it("should not be updated", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);

await userEvent.clear(screen.getByLabelText("Format"));

await userEvent.type(screen.getByLabelText("Format"), "yyYXXYY");

await userEvent.click(
screen.getByRole("button", { name: "Save Date Format" })
);

expect((await screen.findAllByRole("status"))[0]).toHaveTextContent(
`Invalid Date Format!. Please go to https://date-fns.org/docs/format to see valid formats`
);
});

it("should show date format", async () => {
render(
<ApplicationRoot>
<GeneralDataSettings />
</ApplicationRoot>
);
await waitFor(() => {
expect(screen.getByLabelText("Format")).toHaveValue("yyyy MMM do");
});
});
});
});
});
93 changes: 0 additions & 93 deletions src/__tests__/admin/settings/date.spec.tsx

This file was deleted.

23 changes: 17 additions & 6 deletions src/backend/data/data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,13 @@ export class DataApiService implements IDataApiService {
data: Record<string, unknown>,
accountProfile: IAccountProfile
): Promise<void> {
const [allowedFields, primaryField, entityValidations] = await Promise.all([
this._entitiesApiService.getAllowedCrudsFieldsToShow(entity, "update"),
this._entitiesApiService.getEntityPrimaryField(entity),
this._configurationApiService.show("entity_validations", entity),
]);
const [allowedFields, primaryField, entityValidations, metadataColumns] =
await Promise.all([
this._entitiesApiService.getAllowedCrudsFieldsToShow(entity, "update"),
this._entitiesApiService.getEntityPrimaryField(entity),
this._configurationApiService.show("entity_validations", entity),
this._configurationApiService.show("metadata_columns"),
]);

// validate only the fields presents in 'data'
noop(entityValidations);
Expand All @@ -248,12 +250,21 @@ export class DataApiService implements IDataApiService {
dataId: id,
});

const valueToUpdate = this.returnOnlyDataThatAreAllowed(
data,
allowedFields
);

if (allowedFields.includes(metadataColumns.updatedAt)) {
valueToUpdate[metadataColumns.updatedAt] = new Date();
}

await this.getDataAccessInstance().update(
entity,
{
[primaryField]: id,
},
this.returnOnlyDataThatAreAllowed(data, allowedFields)
valueToUpdate
);

await PortalDataHooksService.afterUpdate({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,12 +455,15 @@ describe("Table Filters", () => {

await userEvent.keyboard("{Enter}");

await waitFor(() => {
expect(setFilterValueJestFn).toHaveBeenLastCalledWith({
operator: "i",
value: ["option-1", "option-2"],
});
});
await waitFor(
() => {
expect(setFilterValueJestFn).toHaveBeenLastCalledWith({
operator: "i",
value: ["option-1", "option-2"],
});
},
{ timeout: 5000 }
);

await userEvent.selectOptions(
screen.getByRole("combobox", { name: "Select Filter Operator" }),
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/lib/routing/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const NAVIGATION_LINKS = {
ENTITIES: "/admin/settings/entities",
MENU: "/admin/settings/menu",
SYSTEM: "/admin/settings/system",
DATE: "/admin/settings/date",
DATA: "/admin/settings/data",
SITE: "/admin/settings/site",
THEME: "/admin/settings/theme",
VARIABLES: "/admin/settings/variables",
Expand Down
17 changes: 15 additions & 2 deletions src/frontend/views/data/_BaseEntityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import {
FormSkeletonSchema,
} from "frontend/design-system/components/Skeleton/Form";
import { SchemaForm } from "frontend/components/SchemaForm";
import { useEntityConfiguration } from "frontend/hooks/configuration/configuration.store";
import {
useAppConfiguration,
useEntityConfiguration,
} from "frontend/hooks/configuration/configuration.store";
import {
useEntityFields,
useEntityToOneReferenceFields,
Expand Down Expand Up @@ -67,6 +70,8 @@ export function BaseEntityForm({
);
const entityToOneReferenceFields = useEntityToOneReferenceFields(entity);

const metaDataColumns = useAppConfiguration("metadata_columns");

const error =
entityFieldTypesMap.error ||
hiddenColumns.error ||
Expand All @@ -88,7 +93,15 @@ export function BaseEntityForm({
const viewState = useEntityViewStateMachine(isLoading, error, crudAction);

const fields = filterOutHiddenScalarColumns(
entityFields.data.filter(({ isId }) => !isId),
entityFields.data
.filter(({ isId }) => !isId)
.filter(
({ name }) =>
![
metaDataColumns.data.createdAt,
metaDataColumns.data.updatedAt,
].includes(name)
),
hiddenColumns.data
).map(({ name }) => name);

Expand Down
12 changes: 11 additions & 1 deletion src/frontend/views/entity/Crud/EntityFieldsSelectionSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
useEntitySlug,
} from "frontend/hooks/entity/entity.config";
import { IEntityCrudSettings } from "shared/configurations";
import { useAppConfiguration } from "frontend/hooks/configuration/configuration.store";
import { ENTITY_CRUD_LABELS } from "../constants";
import { makeEntityFieldsSelectionKey } from "./constants";

Expand Down Expand Up @@ -43,6 +44,8 @@ export function EntityFieldsSelectionSettings({

const getEntityFieldLabels = useEntityFieldLabels();

const metaDataColumns = useAppConfiguration("metadata_columns");

const { toggleSelection, allSelections, selectMutiple, isSelected } =
useStringSelections(makeEntityFieldsSelectionKey(entity, crudKey));

Expand Down Expand Up @@ -82,7 +85,14 @@ export function EntityFieldsSelectionSettings({
render={(menuItem) => {
const isHidden = isSelected(menuItem.name);

const disabled = menuItem.isId || !toggling.enabled;
const disabled =
menuItem.isId ||
!toggling.enabled ||
((crudKey === "create" || crudKey === "update") &&
[
metaDataColumns.data.createdAt,
metaDataColumns.data.updatedAt,
].includes(menuItem.name));

return (
<SectionListItem
Expand Down
Loading

0 comments on commit dd79434

Please sign in to comment.