mirror of
https://github.com/stefanpejcic/openpanel
synced 2025-06-26 18:28:26 +00:00
packages
This commit is contained in:
2
packages/core/src/hooks/auditLog/index.ts
Normal file
2
packages/core/src/hooks/auditLog/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./useLog";
|
||||
export * from "./useLogList";
|
||||
424
packages/core/src/hooks/auditLog/useLog/index.spec.ts
Normal file
424
packages/core/src/hooks/auditLog/useLog/index.spec.ts
Normal file
@@ -0,0 +1,424 @@
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
|
||||
import { TestWrapper, mockAuthProvider, mockLegacyAuthProvider } from "@test";
|
||||
|
||||
import { useLog } from ".";
|
||||
import type { LogParams } from "../../../contexts/auditLog/types";
|
||||
import type { ResourceProps } from "../../../contexts/resource/types";
|
||||
import * as hasPermission from "../../../definitions/helpers/hasPermission";
|
||||
|
||||
const auditLogProviderCreateMock = jest.fn();
|
||||
const auditLogProviderUpdateMock = jest.fn();
|
||||
const auditLogProviderGetMock = jest.fn();
|
||||
|
||||
const invalidateQueriesMock = jest.fn();
|
||||
jest.mock("@tanstack/react-query", () => ({
|
||||
...jest.requireActual("@tanstack/react-query"),
|
||||
useQueryClient: () => ({
|
||||
...jest.requireActual("@tanstack/react-query").useQueryClient(),
|
||||
invalidateQueries: invalidateQueriesMock,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("useLog Hook", () => {
|
||||
beforeEach(() => {
|
||||
auditLogProviderCreateMock.mockReset();
|
||||
auditLogProviderUpdateMock.mockReset();
|
||||
auditLogProviderGetMock.mockReset();
|
||||
invalidateQueriesMock.mockReset();
|
||||
});
|
||||
|
||||
describe("log callback", () => {
|
||||
it("should called logEvent empty permission", async () => {
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
},
|
||||
],
|
||||
auditLogProvider: {
|
||||
create: auditLogProviderCreateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { log } = result.current;
|
||||
|
||||
const logEventPayload: LogParams = {
|
||||
action: "create",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
author: {},
|
||||
};
|
||||
|
||||
log.mutate(logEventPayload);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.log.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(auditLogProviderCreateMock).toBeCalledWith(logEventPayload);
|
||||
expect(auditLogProviderCreateMock).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not called logEvent if no includes permissions", async () => {
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
meta: { auditLog: { permissions: ["create"] } },
|
||||
},
|
||||
],
|
||||
auditLogProvider: {
|
||||
get: auditLogProviderGetMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { log } = result.current;
|
||||
|
||||
const logEventPayload: LogParams = {
|
||||
action: "update",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
|
||||
log.mutate(logEventPayload);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.log.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(auditLogProviderGetMock).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should called logEvent if exist auditLogPermissions", async () => {
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
meta: { auditLog: { permissions: ["update"] } },
|
||||
},
|
||||
],
|
||||
auditLogProvider: {
|
||||
create: auditLogProviderCreateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { log } = result.current;
|
||||
|
||||
const logEventPayload: LogParams = {
|
||||
action: "update",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
|
||||
log.mutate(logEventPayload);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.log.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(auditLogProviderCreateMock).toBeCalled();
|
||||
});
|
||||
|
||||
it("should not invoke `useGetIdentity` if `auditLogProvider.create` is not defined", async () => {
|
||||
const getUserIdentityMock = jest.fn();
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
},
|
||||
],
|
||||
authProvider: {
|
||||
check: () => Promise.resolve({ authenticated: true }),
|
||||
login: () => Promise.resolve({ success: true }),
|
||||
logout: () => Promise.resolve({ success: true }),
|
||||
onError: () => Promise.resolve({}),
|
||||
getIdentity: getUserIdentityMock,
|
||||
},
|
||||
auditLogProvider: {},
|
||||
}),
|
||||
});
|
||||
|
||||
const logEventPayload: LogParams = {
|
||||
action: "update",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
|
||||
result.current.log.mutate(logEventPayload);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.log.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(getUserIdentityMock).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("should invoke `useGetIdentity` if `auditLogProvider.create` is defined", async () => {
|
||||
const getUserIdentityMock = jest.fn();
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
},
|
||||
],
|
||||
authProvider: {
|
||||
check: () => Promise.resolve({ authenticated: true }),
|
||||
login: () => Promise.resolve({ success: true }),
|
||||
logout: () => Promise.resolve({ success: true }),
|
||||
onError: () => Promise.resolve({}),
|
||||
getIdentity: getUserIdentityMock,
|
||||
},
|
||||
auditLogProvider: {
|
||||
create: auditLogProviderCreateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const logEventPayload: LogParams = {
|
||||
action: "update",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
|
||||
result.current.log.mutate(logEventPayload);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.log.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(getUserIdentityMock).toBeCalled();
|
||||
});
|
||||
|
||||
it.each(["legacy", "new"])(
|
||||
"should work with %s auth provider",
|
||||
async (testCase) => {
|
||||
const isLegacy = testCase === "legacy";
|
||||
|
||||
const authProvider = isLegacy
|
||||
? {
|
||||
legacyAuthProvider: mockLegacyAuthProvider,
|
||||
}
|
||||
: { authProvider: mockAuthProvider };
|
||||
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
...authProvider,
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
meta: { auditLog: { permissions: ["update"] } },
|
||||
},
|
||||
],
|
||||
auditLogProvider: {
|
||||
create: auditLogProviderCreateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const { log } = result.current;
|
||||
|
||||
const logEventPayload: LogParams = {
|
||||
action: "update",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
};
|
||||
|
||||
log.mutate(logEventPayload);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.log.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
const author = isLegacy
|
||||
? await mockLegacyAuthProvider.getUserIdentity?.()
|
||||
: await mockAuthProvider.getIdentity?.();
|
||||
|
||||
expect(auditLogProviderCreateMock).toHaveBeenCalledWith({
|
||||
...logEventPayload,
|
||||
author,
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
"meta.audit",
|
||||
"options.audit", // deprecated value
|
||||
"options.audit.auditLog.permissions", // deprecated value
|
||||
])("should work with %s values", async (testCase) => {
|
||||
// jest spyon hasPermission
|
||||
const hasPermissionSpy = jest.spyOn(hasPermission, "hasPermission");
|
||||
hasPermissionSpy.mockReturnValue(false);
|
||||
|
||||
let resourceAudit: ResourceProps["meta"] = {};
|
||||
|
||||
switch (testCase) {
|
||||
case "meta.audit":
|
||||
resourceAudit = {
|
||||
meta: { audit: ["create"] },
|
||||
};
|
||||
break;
|
||||
case "options.audit":
|
||||
resourceAudit = {
|
||||
options: { audit: ["create"] },
|
||||
};
|
||||
break;
|
||||
case "options.audit.auditLog.permissions":
|
||||
resourceAudit = {
|
||||
options: {
|
||||
auditLog: {
|
||||
permissions: ["create"],
|
||||
},
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
...resourceAudit,
|
||||
},
|
||||
],
|
||||
auditLogProvider: {
|
||||
create: auditLogProviderCreateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await result.current.log.mutateAsync({
|
||||
action: "create",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(auditLogProviderCreateMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return undefined if logPermissions exist but hasPermission returns false", async () => {
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
resources: [
|
||||
{
|
||||
name: "posts",
|
||||
meta: {
|
||||
audit: ["update"],
|
||||
},
|
||||
},
|
||||
],
|
||||
auditLogProvider: {
|
||||
create: auditLogProviderCreateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await result.current.log.mutateAsync({
|
||||
action: "create",
|
||||
resource: "posts",
|
||||
data: { id: 1, title: "title" },
|
||||
meta: {
|
||||
id: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(auditLogProviderCreateMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("rename mutation", () => {
|
||||
it("succeed rename", async () => {
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
auditLogProvider: {
|
||||
update: auditLogProviderUpdateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const { rename } = result.current;
|
||||
const { mutate } = rename;
|
||||
|
||||
auditLogProviderUpdateMock.mockResolvedValueOnce({
|
||||
id: 1,
|
||||
name: "test name",
|
||||
resource: "posts",
|
||||
});
|
||||
|
||||
mutate({ id: 1, name: "test name" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.rename.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(auditLogProviderUpdateMock).toBeCalledWith({
|
||||
id: 1,
|
||||
name: "test name",
|
||||
});
|
||||
expect(auditLogProviderUpdateMock).toBeCalledTimes(1);
|
||||
|
||||
expect(invalidateQueriesMock).toBeCalledTimes(1);
|
||||
expect(invalidateQueriesMock).toBeCalledWith(["logList", "posts"]);
|
||||
});
|
||||
|
||||
it("succeed rename should not call invalidateQueries if have not resource", async () => {
|
||||
const { result } = renderHook(() => useLog(), {
|
||||
wrapper: TestWrapper({
|
||||
auditLogProvider: {
|
||||
update: auditLogProviderUpdateMock,
|
||||
},
|
||||
}),
|
||||
});
|
||||
const { rename } = result.current;
|
||||
const { mutate } = rename;
|
||||
|
||||
auditLogProviderUpdateMock.mockResolvedValueOnce({
|
||||
id: 1,
|
||||
name: "test name",
|
||||
});
|
||||
|
||||
mutate({ id: 1, name: "test name" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.rename.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(auditLogProviderUpdateMock).toBeCalledWith({
|
||||
id: 1,
|
||||
name: "test name",
|
||||
});
|
||||
expect(auditLogProviderUpdateMock).toBeCalledTimes(1);
|
||||
|
||||
expect(invalidateQueriesMock).toBeCalledTimes(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
158
packages/core/src/hooks/auditLog/useLog/index.ts
Normal file
158
packages/core/src/hooks/auditLog/useLog/index.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { useContext } from "react";
|
||||
|
||||
import { getXRay } from "@refinedev/devtools-internal";
|
||||
import {
|
||||
type UseMutationOptions,
|
||||
type UseMutationResult,
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import { AuditLogContext } from "@contexts/auditLog";
|
||||
import { ResourceContext } from "@contexts/resource";
|
||||
import { hasPermission, pickNotDeprecated } from "@definitions/helpers";
|
||||
import { useActiveAuthProvider } from "@definitions/helpers";
|
||||
import { pickResource } from "@definitions/helpers/pick-resource";
|
||||
import { useGetIdentity } from "@hooks/auth";
|
||||
import { useKeys } from "@hooks/useKeys";
|
||||
|
||||
import type { LogParams } from "../../../contexts/auditLog/types";
|
||||
import type { BaseKey } from "../../../contexts/data/types";
|
||||
|
||||
type LogRenameData =
|
||||
| {
|
||||
resource?: string;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
export type UseLogReturnType<TLogData, TLogRenameData> = {
|
||||
log: UseMutationResult<TLogData, Error, LogParams>;
|
||||
rename: UseMutationResult<
|
||||
TLogRenameData,
|
||||
Error,
|
||||
{
|
||||
id: BaseKey;
|
||||
name: string;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
export type UseLogMutationProps<
|
||||
TLogData,
|
||||
TLogRenameData extends LogRenameData = LogRenameData,
|
||||
> = {
|
||||
logMutationOptions?: Omit<
|
||||
UseMutationOptions<TLogData, Error, LogParams, unknown>,
|
||||
"mutationFn"
|
||||
>;
|
||||
renameMutationOptions?: Omit<
|
||||
UseMutationOptions<
|
||||
TLogRenameData,
|
||||
Error,
|
||||
{ id: BaseKey; name: string },
|
||||
unknown
|
||||
>,
|
||||
"mutationFn" | "onSuccess"
|
||||
>;
|
||||
};
|
||||
|
||||
/**
|
||||
* useLog is used to `create` a new and `rename` the existing audit log.
|
||||
* @see {@link https://refine.dev/docs/api-reference/core/hooks/audit-log/useLog} for more details.
|
||||
*/
|
||||
|
||||
export const useLog = <
|
||||
TLogData,
|
||||
TLogRenameData extends LogRenameData = LogRenameData,
|
||||
>({
|
||||
logMutationOptions,
|
||||
renameMutationOptions,
|
||||
}: UseLogMutationProps<TLogData, TLogRenameData> = {}): UseLogReturnType<
|
||||
TLogData,
|
||||
TLogRenameData
|
||||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
const auditLogContext = useContext(AuditLogContext);
|
||||
const { keys, preferLegacyKeys } = useKeys();
|
||||
|
||||
const authProvider = useActiveAuthProvider();
|
||||
|
||||
const { resources } = useContext(ResourceContext);
|
||||
const {
|
||||
data: identityData,
|
||||
refetch,
|
||||
isLoading,
|
||||
} = useGetIdentity({
|
||||
v3LegacyAuthProviderCompatible: Boolean(authProvider?.isLegacy),
|
||||
queryOptions: {
|
||||
enabled: !!auditLogContext?.create,
|
||||
},
|
||||
});
|
||||
|
||||
const log = useMutation<TLogData, Error, LogParams, unknown>(
|
||||
async (params) => {
|
||||
const resource = pickResource(params.resource, resources);
|
||||
const logPermissions = pickNotDeprecated(
|
||||
resource?.meta?.audit,
|
||||
resource?.options?.audit,
|
||||
resource?.options?.auditLog?.permissions,
|
||||
);
|
||||
|
||||
if (logPermissions) {
|
||||
if (!hasPermission(logPermissions, params.action)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let authorData;
|
||||
if (isLoading && !!auditLogContext?.create) {
|
||||
authorData = await refetch();
|
||||
}
|
||||
|
||||
return await auditLogContext.create?.({
|
||||
...params,
|
||||
author: identityData ?? authorData?.data,
|
||||
});
|
||||
},
|
||||
{
|
||||
mutationKey: keys().audit().action("log").get(),
|
||||
...logMutationOptions,
|
||||
meta: {
|
||||
...logMutationOptions?.meta,
|
||||
...getXRay("useLog", preferLegacyKeys),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const rename = useMutation<
|
||||
TLogRenameData,
|
||||
Error,
|
||||
{ id: BaseKey; name: string },
|
||||
unknown
|
||||
>(
|
||||
async (params) => {
|
||||
return await auditLogContext.update?.(params);
|
||||
},
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
if (data?.resource) {
|
||||
queryClient.invalidateQueries(
|
||||
keys()
|
||||
.audit()
|
||||
.resource(data?.resource ?? "")
|
||||
.action("list")
|
||||
.get(preferLegacyKeys),
|
||||
);
|
||||
}
|
||||
},
|
||||
mutationKey: keys().audit().action("rename").get(),
|
||||
...renameMutationOptions,
|
||||
meta: {
|
||||
...renameMutationOptions?.meta,
|
||||
...getXRay("useLog", preferLegacyKeys),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return { log, rename };
|
||||
};
|
||||
159
packages/core/src/hooks/auditLog/useLogList/index.spec.ts
Normal file
159
packages/core/src/hooks/auditLog/useLogList/index.spec.ts
Normal file
@@ -0,0 +1,159 @@
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import { TestWrapper, queryClient } from "@test";
|
||||
|
||||
import { useLogList } from "./";
|
||||
|
||||
const auditLogProviderGetMock = jest.fn();
|
||||
|
||||
describe("useLogList Hook", () => {
|
||||
beforeEach(() => {
|
||||
auditLogProviderGetMock.mockReset();
|
||||
});
|
||||
|
||||
it("useLogList should call the auditLogProvider's list method with same properties", async () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useLogList({
|
||||
resource: "posts",
|
||||
action: "list",
|
||||
meta: { id: 1 },
|
||||
metaData: { fields: ["id", "action", "data"] },
|
||||
}),
|
||||
{
|
||||
wrapper: TestWrapper({
|
||||
auditLogProvider: {
|
||||
get: auditLogProviderGetMock,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isFetched).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(auditLogProviderGetMock).toBeCalledWith({
|
||||
resource: "posts",
|
||||
action: "list",
|
||||
meta: { id: 1 },
|
||||
metaData: { fields: ["id", "action", "data"] },
|
||||
});
|
||||
});
|
||||
|
||||
it("useLogList should return data with 'posts' resource", async () => {
|
||||
const { result } = renderHook(() => useLogList({ resource: "posts" }), {
|
||||
wrapper: TestWrapper({
|
||||
auditLogProvider: {
|
||||
get: ({ resource }) => {
|
||||
if (resource === "posts") {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: 1,
|
||||
action: "create",
|
||||
data: { id: 1, title: "title" },
|
||||
},
|
||||
]);
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isFetched).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(result.current?.data).toStrictEqual([
|
||||
{
|
||||
id: 1,
|
||||
action: "create",
|
||||
data: { id: 1, title: "title" },
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should override `queryKey` with `queryOptions.queryKey`", async () => {
|
||||
const getMock = jest.fn().mockResolvedValue([
|
||||
{
|
||||
id: 1,
|
||||
action: "create",
|
||||
data: { id: 1, title: "title" },
|
||||
},
|
||||
]);
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useLogList({
|
||||
resource: "posts",
|
||||
action: "list",
|
||||
meta: { id: 1 },
|
||||
metaData: { fields: ["id", "action", "data"] },
|
||||
queryOptions: {
|
||||
queryKey: ["foo", "bar"],
|
||||
},
|
||||
}),
|
||||
{
|
||||
wrapper: TestWrapper({
|
||||
auditLogProvider: {
|
||||
get: getMock,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(
|
||||
queryClient.getQueryCache().findAll({
|
||||
queryKey: ["foo", "bar"],
|
||||
}),
|
||||
).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should override `queryFn` with `queryOptions.queryFn`", async () => {
|
||||
const getMock = jest.fn().mockResolvedValue([
|
||||
{
|
||||
id: 1,
|
||||
action: "create",
|
||||
data: { id: 1, title: "title" },
|
||||
},
|
||||
]);
|
||||
const queryFnMock = jest.fn().mockResolvedValue([
|
||||
{
|
||||
id: 1,
|
||||
action: "create",
|
||||
data: { id: 1, title: "title" },
|
||||
},
|
||||
]);
|
||||
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useLogList({
|
||||
resource: "posts",
|
||||
action: "list",
|
||||
meta: { id: 1 },
|
||||
metaData: { fields: ["id", "action", "data"] },
|
||||
queryOptions: {
|
||||
queryFn: queryFnMock,
|
||||
},
|
||||
}),
|
||||
{
|
||||
wrapper: TestWrapper({
|
||||
auditLogProvider: {
|
||||
get: getMock,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isSuccess).toBeTruthy();
|
||||
});
|
||||
|
||||
expect(getMock).not.toBeCalled();
|
||||
expect(queryFnMock).toBeCalled();
|
||||
});
|
||||
});
|
||||
74
packages/core/src/hooks/auditLog/useLogList/index.ts
Normal file
74
packages/core/src/hooks/auditLog/useLogList/index.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { useContext } from "react";
|
||||
|
||||
import { getXRay } from "@refinedev/devtools-internal";
|
||||
import {
|
||||
type UseQueryOptions,
|
||||
type UseQueryResult,
|
||||
useQuery,
|
||||
} from "@tanstack/react-query";
|
||||
|
||||
import { AuditLogContext } from "@contexts/auditLog";
|
||||
import { useKeys } from "@hooks/useKeys";
|
||||
|
||||
import type { HttpError, MetaQuery } from "../../../contexts/data/types";
|
||||
|
||||
export type UseLogProps<TQueryFnData, TError, TData> = {
|
||||
resource: string;
|
||||
action?: string;
|
||||
meta?: Record<number | string, any>;
|
||||
author?: Record<number | string, any>;
|
||||
queryOptions?: UseQueryOptions<TQueryFnData, TError, TData>;
|
||||
metaData?: MetaQuery;
|
||||
};
|
||||
|
||||
/**
|
||||
* useLogList is used to get and filter audit logs.
|
||||
*
|
||||
* @see {@link https://refine.dev/docs/api-reference/core/hooks/audit-log/useLogList} for more details.
|
||||
*
|
||||
* @typeParam TQueryFnData - Result data returned by the query function.
|
||||
* @typeParam TError - Custom error object that extends {@link https://refine.dev/docs/api-reference/core/interfaceReferences#httperror `HttpError`}
|
||||
* @typeParam TData - Result data returned by the `select` function. Defaults to `TQueryFnData`
|
||||
*
|
||||
*/
|
||||
export const useLogList = <
|
||||
TQueryFnData = any,
|
||||
TError extends HttpError = HttpError,
|
||||
TData = TQueryFnData,
|
||||
>({
|
||||
resource,
|
||||
action,
|
||||
meta,
|
||||
author,
|
||||
metaData,
|
||||
queryOptions,
|
||||
}: UseLogProps<TQueryFnData, TError, TData>): UseQueryResult<TData> => {
|
||||
const { get } = useContext(AuditLogContext);
|
||||
const { keys, preferLegacyKeys } = useKeys();
|
||||
|
||||
const queryResponse = useQuery<TQueryFnData, TError, TData>({
|
||||
queryKey: keys()
|
||||
.audit()
|
||||
.resource(resource)
|
||||
.action("list")
|
||||
.params(meta)
|
||||
.get(preferLegacyKeys),
|
||||
queryFn: () =>
|
||||
get?.({
|
||||
resource,
|
||||
action,
|
||||
author,
|
||||
meta,
|
||||
metaData,
|
||||
}) ?? Promise.resolve([]),
|
||||
enabled: typeof get !== "undefined",
|
||||
...queryOptions,
|
||||
retry: false,
|
||||
meta: {
|
||||
...queryOptions?.meta,
|
||||
...getXRay("useLogList", preferLegacyKeys, resource),
|
||||
},
|
||||
});
|
||||
|
||||
return queryResponse;
|
||||
};
|
||||
Reference in New Issue
Block a user