Skip to content

Commit

Permalink
Merge pull request #136 from premieroctet/fix/deep-relation-formatter
Browse files Browse the repository at this point in the history
Add deep access for relationship formatter
  • Loading branch information
cregourd authored Jan 31, 2024
2 parents fcc7c66 + 41483ee commit 9e62a55
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 133 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-nails-attack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@premieroctet/next-admin": patch
---

Add deep access for relationship formatter
6 changes: 3 additions & 3 deletions packages/next-admin/src/tests/serverUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("fillRelationInSchema", () => {
metadata: null
},
]);
const result = await fillRelationInSchema(schema, prismaMock, "Post", {});
const result = await fillRelationInSchema(prismaMock, "Post", {})(schema)
expect(result.definitions.Post.properties.author?.enum).toEqual([
{ label: 1, value: 1 },
{ label: 2, value: 2 },
Expand All @@ -43,14 +43,14 @@ describe("transformSchema", () => {
const userEditOptions = options.model?.User?.edit!;

it("should return the schema with the new format", async () => {
const result = changeFormatInSchema(schema, "User", userEditOptions);
const result = changeFormatInSchema("User", userEditOptions)(schema);
expect(result.definitions.User.properties.birthDate?.format).toEqual(
"date"
);
});

it("should return the schema without the hidden properties", async () => {
const result = removeHiddenProperties(schema, "User", userEditOptions);
const result = removeHiddenProperties("User", userEditOptions)(schema)
expect(result.definitions.User.properties).not.toHaveProperty("createdAt");
expect(result.definitions.User.properties).not.toHaveProperty("updatedAt");
});
Expand Down
6 changes: 3 additions & 3 deletions packages/next-admin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type PropertyPayload<M extends ModelName, P extends keyof ObjectField<M>>
Prisma.TypeMap["model"][M]["payload"]["objects"][P] extends infer T | null ? T : never |
Prisma.TypeMap["model"][M]["payload"]["objects"][P]

export type ModelFromProperty<M extends ModelName, P extends keyof ObjectField<M>> = PropertyPayload<M, P> extends Payload ? ModelFromPayload<PropertyPayload<M,P>> : never
export type ModelFromProperty<M extends ModelName, P extends keyof ObjectField<M>> = PropertyPayload<M, P> extends Payload ? ModelFromPayload<PropertyPayload<M, P>> : never

export type ModelWithoutRelationships<M extends ModelName> = Model<M, number>;

Expand All @@ -60,7 +60,7 @@ export type Field<P extends ModelName> = keyof Model<P>;

export type ListFieldsOptions<T extends ModelName> = {
[P in Field<T>]?: {
formatter?: (item: Model<T>[P], context?: NextAdminContext) => ReactNode;
formatter?: (item: P extends keyof ObjectField<T> ? ModelFromProperty<T, P> : Model<T>[P], context?: NextAdminContext) => ReactNode;
};
};

Expand Down Expand Up @@ -184,7 +184,7 @@ export type Order<M extends ModelName> = {
export type Select<M extends ModelName> = {
[P in Field<M>]?: boolean;
} & {
_count: {
_count?: {
select: {
[key in string]: boolean;
};
Expand Down
91 changes: 53 additions & 38 deletions packages/next-admin/src/utils/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Prisma, PrismaClient } from "@prisma/client";
import { ITEMS_PER_PAGE } from "../config";
import {
EditOptions,
Field,
ListOptions,
ModelName,
Expand All @@ -23,23 +24,23 @@ export const createWherePredicate = (
) => {
return search
? {
OR: fieldsFiltered
?.filter((field) => field.kind === "scalar")
.map((field) => {
if (field.type === "String") {
// @ts-ignore
const mode = Prisma?.QueryMode ? { mode: Prisma.QueryMode.insensitive } : {};
return {
[field.name]: { contains: search, ...mode },
};
}
if (field.type === "Int" && !isNaN(Number(search))) {
return { [field.name]: Number(search) };
}
return null;
})
.filter(Boolean),
}
OR: fieldsFiltered
?.filter((field) => field.kind === "scalar")
.map((field) => {
if (field.type === "String") {
// @ts-ignore
const mode = Prisma?.QueryMode ? { mode: Prisma.QueryMode.insensitive } : {};
return {
[field.name]: { contains: search, ...mode },
};
}
if (field.type === "Int" && !isNaN(Number(search))) {
return { [field.name]: Number(search) };
}
return null;
})
.filter(Boolean),
}
: {};
};

Expand Down Expand Up @@ -80,22 +81,7 @@ export const preparePrismaListRequest = <M extends ModelName>(
let fieldsFiltered = model?.fields;
const list = options?.model?.[resource]?.list as ListOptions<M>;
if (list) {
const listDisplayedKeys = list.display;
select = listDisplayedKeys?.reduce(
(acc, column) => {
const field = model?.fields.find(({ name }) => name === column);
if (field?.kind === "object" && field?.isList === true) {
if (!acc._count) acc._count = { select: {} };
acc._count.select = { ...acc._count.select, [column]: true };
} else {
// @ts-expect-error
acc[column] = true;
}
return acc;
},
{ [getModelIdProperty(resource)]: true } as Select<M>
);

select = selectPayloadForModel(resource, list, 'object');
fieldsFiltered =
model?.fields.filter(({ name }) =>
list.search?.includes(name as Field<M>)
Expand Down Expand Up @@ -157,11 +143,9 @@ export const getMappedDataList = async (
data.forEach((item, index) => {
Object.keys(item).forEach((key) => {
let itemValue;

const model = capitalize(key) as ModelName;
const idProperty = getModelIdProperty(model);
if (typeof item[key] === "object" && item[key] !== null) {
const model = capitalize(key) as ModelName;
const idProperty = getModelIdProperty(model);

switch (item[key].type) {
case "link":
itemValue = item[key].value.label;
Expand Down Expand Up @@ -196,10 +180,18 @@ export const getMappedDataList = async (
item[key].__nextadmin_formatted = listFields[
key as keyof typeof listFields
// @ts-expect-error
]?.formatter?.(itemValue ?? item[key], context);
]?.formatter?.(itemValue ?? item[key], context)
} else {
if (typeof item[key]?.__nextadmin_formatted === 'object') {
item[key].__nextadmin_formatted = item[key].__nextadmin_formatted[idProperty]
}
data[index][key] = item[key];
}

if (typeof item[key]?.value === 'object') {
item[key].value.label = item[key].value.label[idProperty]
}

});
});

Expand All @@ -209,3 +201,26 @@ export const getMappedDataList = async (
error,
};
};

export const selectPayloadForModel = <M extends ModelName>(resource: M, options?: EditOptions<M> | ListOptions<M>, level: 'scalar' | 'object' = 'scalar') => {
const model = getPrismaModelForResource(resource);
const idProperty = getModelIdProperty(resource);

const displayKeys = options?.display;

let selectedFields = model?.fields.reduce(
(acc, field) => {
if ((displayKeys && displayKeys.includes(field.name as Field<M>) || !displayKeys)) {
if (level === 'object' && field.kind === 'object') {
acc[field.name] = { select: selectPayloadForModel(field.type as ModelName, {}, 'scalar') };
} else {
acc[field.name] = true;
}
}
return acc;
},
{ [idProperty]: true } as any
);

return selectedFields;
}
40 changes: 6 additions & 34 deletions packages/next-admin/src/utils/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ import {
} from "../types";
import { createBoundServerAction } from "./actions";
import { getCustomInputs } from "./options";
import { getMappedDataList } from "./prisma";
import { getMappedDataList, selectPayloadForModel } from "./prisma";
import {
fillRelationInSchema,
getModelIdProperty,
getPrismaModelForResource,
getResourceFromParams,
getResourceIdFromParam,
getResources,
orderSchema,
transformData,
transformSchema,
transformSchema
} from "./server";

export type GetPropsFromParamsParams = {
Expand Down Expand Up @@ -176,50 +174,24 @@ export async function getPropsFromParams({
}
case Page.EDIT: {
const resourceId = getResourceIdFromParam(params[1], resource);
const model = getPrismaModelForResource(resource);
const idProperty = getModelIdProperty(resource);
let selectedFields = model?.fields.reduce(
(acc, field) => {
acc[field.name] = true;
return acc;
},
{ [idProperty]: true }
);

const dmmfSchema = getPrismaModelForResource(resource);
const edit = options?.model?.[resource]?.edit as EditOptions<
typeof resource
>;

let deepCopySchema = cloneDeep(schema);
deepCopySchema = transformSchema(deepCopySchema, resource, edit);
deepCopySchema = await fillRelationInSchema(
deepCopySchema,
prisma,
resource,
searchParams,
options,
);
deepCopySchema = orderSchema(deepCopySchema, resource, options);


let deepCopySchema = await transformSchema(resource, edit, prisma, searchParams, options)(cloneDeep(schema));
const customInputs = isAppDir
? getCustomInputs(resource, options)
: undefined;

if (resourceId !== undefined) {
const editDisplayedKeys = edit?.display;
const editSelect = editDisplayedKeys?.reduce(
(acc, column) => {
acc[column] = true;
return acc;
},
{ [idProperty]: true }
);
selectedFields = editSelect ?? selectedFields;
const select = selectPayloadForModel(resource, edit, "object");
// @ts-expect-error
let data = await prisma[resource].findUniqueOrThrow({
select,
where: { [idProperty]: resourceId },
select: selectedFields,
});
data = transformData(data, resource, edit, options);
return {
Expand Down
Loading

0 comments on commit 9e62a55

Please sign in to comment.