Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 0 additions & 63 deletions packages/agent-client/src/domains/remote-agent-client.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,24 @@
import type { ActionEndpointsByCollection } from './action';
import type HttpRequester from '../http-requester';
import type { RawTreeWithSources } from '@forestadmin/forestadmin-client';

import Chart from './chart';
import Collection from './collection';

export type SmartActionPermissionsOverride = Partial<{
triggerEnabled: boolean;
triggerConditions: RawTreeWithSources;
approvalRequired: boolean;
approvalRequiredConditions: RawTreeWithSources;
userApprovalEnabled: boolean;
userApprovalConditions: RawTreeWithSources;
selfApprovalEnabled: boolean;
}>;

export type CollectionPermissionsOverride = Partial<{
browseEnabled: boolean;
deleteEnabled: boolean;
editEnabled: boolean;
exportEnabled: boolean;
addEnabled: boolean;
readEnabled: boolean;
}>;

export type PermissionsOverride = Record<
string,
{
collection: CollectionPermissionsOverride;
actions: Record<string, SmartActionPermissionsOverride>;
}
>;

type CollectionName<T> = keyof T & string;

export default class RemoteAgentClient<
TypingsSchema extends Record<string, unknown> = Record<string, unknown>,
> extends Chart {
protected actionEndpoints?: ActionEndpointsByCollection;

private overridePermissions?: (permissions: PermissionsOverride) => Promise<void>;

constructor(params?: {
actionEndpoints?: ActionEndpointsByCollection;
httpRequester: HttpRequester;
overridePermissions?: (permissions: PermissionsOverride) => Promise<void>;
}) {
super();
if (!params) return;
this.httpRequester = params.httpRequester;
this.actionEndpoints = params.actionEndpoints;
this.overridePermissions = params.overridePermissions;
}

async overrideCollectionPermission(
collectionName: CollectionName<TypingsSchema>,
permissions: CollectionPermissionsOverride,
) {
await this.overridePermissions?.({
[collectionName]: {
collection: permissions,
actions: {},
},
});
}

async overrideActionPermission(
collectionName: CollectionName<TypingsSchema>,
actionName: string,
permissions: SmartActionPermissionsOverride,
) {
await this.overridePermissions?.({
[collectionName]: {
collection: {},
actions: {
[actionName]: permissions,
},
},
});
}

async clearPermissionOverride() {
await this.overridePermissions?.({});
}

collection(name: CollectionName<TypingsSchema>): Collection {
Expand Down
14 changes: 1 addition & 13 deletions packages/agent-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import type { ActionEndpointsByCollection } from './domains/action';
import type {
CollectionPermissionsOverride,
PermissionsOverride,
SmartActionPermissionsOverride,
} from './domains/remote-agent-client';

import ActionFieldJson from './action-fields/action-field-json';
import ActionFieldStringList from './action-fields/action-field-string-list';
Expand All @@ -20,15 +15,9 @@ export {
ActionRequiresApprovalError,
ActionFormValidationError,
};
export type {
ActionEndpointsByCollection,
CollectionPermissionsOverride,
PermissionsOverride,
SmartActionPermissionsOverride,
};
export type { ActionEndpointsByCollection };

export function createRemoteAgentClient(params: {
overridePermissions?: (permissions: PermissionsOverride) => Promise<void>;
actionEndpoints?: ActionEndpointsByCollection;
token?: string;
url: string;
Expand All @@ -38,7 +27,6 @@ export function createRemoteAgentClient(params: {
return new RemoteAgentClient({
actionEndpoints: params.actionEndpoints,
httpRequester,
overridePermissions: params.overridePermissions,
});
}

Expand Down
81 changes: 0 additions & 81 deletions packages/agent-client/test/domains/remote-agent-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ jest.mock('../../src/http-requester');
describe('RemoteAgentClient', () => {
let httpRequester: jest.Mocked<HttpRequester>;
let client: RemoteAgentClient;
let overridePermissionsMock: jest.Mock;

beforeEach(() => {
jest.clearAllMocks();
httpRequester = {
query: jest.fn(),
stream: jest.fn(),
} as any;
overridePermissionsMock = jest.fn().mockResolvedValue(undefined);
client = new RemoteAgentClient({
httpRequester,
actionEndpoints: {
Expand All @@ -29,7 +27,6 @@ describe('RemoteAgentClient', () => {
},
},
},
overridePermissions: overridePermissionsMock,
});
});

Expand All @@ -55,82 +52,4 @@ describe('RemoteAgentClient', () => {
expect(collection).toBeDefined();
});
});

describe('overrideCollectionPermission', () => {
it('should call overridePermissions with collection permissions', async () => {
await client.overrideCollectionPermission('users', {
browseEnabled: true,
editEnabled: false,
});

expect(overridePermissionsMock).toHaveBeenCalledWith({
users: {
collection: {
browseEnabled: true,
editEnabled: false,
},
actions: {},
},
});
});

it('should not throw if overridePermissions is not provided', async () => {
const clientWithoutOverride = new RemoteAgentClient({
httpRequester,
});

await expect(
clientWithoutOverride.overrideCollectionPermission('users', { browseEnabled: true }),
).resolves.toBeUndefined();
});
});

describe('overrideActionPermission', () => {
it('should call overridePermissions with action permissions', async () => {
await client.overrideActionPermission('users', 'sendEmail', {
triggerEnabled: true,
approvalRequired: true,
});

expect(overridePermissionsMock).toHaveBeenCalledWith({
users: {
collection: {},
actions: {
sendEmail: {
triggerEnabled: true,
approvalRequired: true,
},
},
},
});
});

it('should not throw if overridePermissions is not provided', async () => {
const clientWithoutOverride = new RemoteAgentClient({
httpRequester,
});

await expect(
clientWithoutOverride.overrideActionPermission('users', 'sendEmail', {
triggerEnabled: true,
}),
).resolves.toBeUndefined();
});
});

describe('clearPermissionOverride', () => {
it('should call overridePermissions with empty object', async () => {
await client.clearPermissionOverride();

expect(overridePermissionsMock).toHaveBeenCalledWith({});
});

it('should not throw if overridePermissions is not provided', async () => {
const clientWithoutOverride = new RemoteAgentClient({
httpRequester,
});

await expect(clientWithoutOverride.clearPermissionOverride()).resolves.toBeUndefined();
});
});
});
19 changes: 0 additions & 19 deletions packages/agent-client/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,6 @@ describe('createRemoteAgentClient', () => {
expect(collection).toBeDefined();
});

it('should pass overridePermissions function to the client', async () => {
const overridePermissions = jest.fn().mockResolvedValue(undefined);

const client = createRemoteAgentClient({
url: 'https://api.example.com',
token: 'test-token',
overridePermissions,
});

await client.overrideCollectionPermission('users' as any, { browseEnabled: true });

expect(overridePermissions).toHaveBeenCalledWith({
users: {
collection: { browseEnabled: true },
actions: {},
},
});
});

it('should provide a working client that can access collections', () => {
const client = createRemoteAgentClient({
url: 'https://api.example.com',
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-testing/src/forest-server-sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {
CollectionPermissionsOverride,
PermissionsOverride,
SmartActionPermissionsOverride,
} from '@forestadmin/agent-client';
} from './permission-overrides';
import type { ForestSchema } from '@forestadmin/forestadmin-client';
import type {
EnvironmentCollectionPermissionsV4,
Expand Down
2 changes: 1 addition & 1 deletion packages/agent-testing/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { PermissionsOverride } from './permission-overrides';
import type { TestableAgentOptions } from './types';
import type { Agent } from '@forestadmin/agent';
import type { PermissionsOverride } from '@forestadmin/agent-client';
import type { TSchema } from '@forestadmin/datasource-customizer';
import type { ForestSchema } from '@forestadmin/forestadmin-client';

Expand Down Expand Up @@ -104,7 +104,7 @@
// 0 is a random port
port: options?.port || 0,
// Cast to any to avoid type mismatch when workspace has different forestadmin-client version
forestAdminClient: new ForestAdminClientMock() as any,

Check warning on line 107 in packages/agent-testing/src/index.ts

View workflow job for this annotation

GitHub Actions / Linting & Testing (agent-testing)

Unexpected any. Specify a different type
authSecret: options?.authSecret || 'b0bdf0a639c16bae8851dd24ee3d79ef0a352e957c5b86cb',
envSecret:
options?.envSecret || 'ceba742f5bc73946b34da192816a4d7177b3233fee7769955c29c0e90fd584f2',
Expand Down
28 changes: 28 additions & 0 deletions packages/agent-testing/src/permission-overrides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { RawTreeWithSources } from '@forestadmin/forestadmin-client';

export type SmartActionPermissionsOverride = Partial<{
triggerEnabled: boolean;
triggerConditions: RawTreeWithSources;
approvalRequired: boolean;
approvalRequiredConditions: RawTreeWithSources;
userApprovalEnabled: boolean;
userApprovalConditions: RawTreeWithSources;
selfApprovalEnabled: boolean;
}>;

export type CollectionPermissionsOverride = Partial<{
browseEnabled: boolean;
deleteEnabled: boolean;
editEnabled: boolean;
exportEnabled: boolean;
addEnabled: boolean;
readEnabled: boolean;
}>;

export type PermissionsOverride = Record<
string,
{
collection: CollectionPermissionsOverride;
actions: Record<string, SmartActionPermissionsOverride>;
}
>;
50 changes: 50 additions & 0 deletions packages/agent-testing/src/testable-agent-base.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
import type {
CollectionPermissionsOverride,
PermissionsOverride,
SmartActionPermissionsOverride,
} from './permission-overrides';
import type { ActionEndpointsByCollection, HttpRequester } from '@forestadmin/agent-client';
import type { TSchema } from '@forestadmin/datasource-customizer';

import { RemoteAgentClient } from '@forestadmin/agent-client';

import Benchmark from './benchmark';

type CollectionName<T> = keyof T & string;

export default class TestableAgentBase<
TypingsSchema extends TSchema = TSchema,
> extends RemoteAgentClient<TypingsSchema> {
private readonly overridePermissions?: (permissions: PermissionsOverride) => Promise<void>;

constructor(params?: {
actionEndpoints?: ActionEndpointsByCollection;
httpRequester: HttpRequester;
overridePermissions?: (permissions: PermissionsOverride) => Promise<void>;
}) {
super(params);
this.overridePermissions = params?.overridePermissions;
}

async overrideCollectionPermission(
collectionName: CollectionName<TypingsSchema>,
permissions: CollectionPermissionsOverride,
) {
await this.overridePermissions?.({
[collectionName]: {
collection: permissions,
actions: {},
},
});
}

async overrideActionPermission(
collectionName: CollectionName<TypingsSchema>,
actionName: string,
permissions: SmartActionPermissionsOverride,
) {
await this.overridePermissions?.({
[collectionName]: {
collection: {},
actions: {
[actionName]: permissions,
},
},
});
}

async clearPermissionOverride() {
await this.overridePermissions?.({});
}

benchmark(): Benchmark {
return new Benchmark();
}
Expand Down
Loading
Loading