Changing over permission rules params API to accept a single object

Signed-off-by: Harry Hogg <hhogg@spotify.com>
This commit is contained in:
Harry Hogg
2022-10-03 10:19:47 +01:00
parent 1893c3d4da
commit 6d447843fa
31 changed files with 495 additions and 235 deletions
@@ -31,25 +31,28 @@ const testIntegration = () =>
name: 'testRule1',
description: 'Test rule 1',
resourceType: 'test-resource',
schema: z.tuple([z.string(), z.number()]),
apply: jest.fn(
(_resource: any, _firstParam: string, _secondParam: number) => true,
),
toQuery: jest.fn((firstParam: string, secondParam: number) => ({
schema: z.object({
foo: z.string(),
bar: z.number(),
}),
apply: (_resource: any, _params) => true,
toQuery: params => ({
query: 'testRule1',
params: [firstParam, secondParam],
})),
params,
}),
}),
testRule2: createPermissionRule({
name: 'testRule2',
description: 'Test rule 2',
resourceType: 'test-resource',
schema: z.tuple([z.object({})]),
apply: jest.fn((_resource: any, _firstParam: object) => false),
toQuery: jest.fn((firstParam: object) => ({
schema: z.object({
foo: z.object({}),
}),
apply: (_resource: any) => false,
toQuery: params => ({
query: 'testRule2',
params: [firstParam],
})),
params,
}),
}),
},
});
@@ -59,16 +62,26 @@ describe('createConditionExports', () => {
it('creates condition factories for the supplied rules', () => {
const { conditions } = testIntegration();
expect(conditions.testRule1('a', 1)).toEqual({
expect(
conditions.testRule1({
foo: 'a',
bar: 1,
}),
).toEqual({
rule: 'testRule1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
});
expect(conditions.testRule2({ baz: 'quux' })).toEqual({
expect(conditions.testRule2({ foo: { baz: 'quux' } })).toEqual({
rule: 'testRule2',
resourceType: 'test-resource',
params: [{ baz: 'quux' }],
params: {
foo: { baz: 'quux' },
},
});
});
});
@@ -88,7 +101,10 @@ describe('createConditionExports', () => {
{
rule: 'testRule1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
],
}),
@@ -101,7 +117,10 @@ describe('createConditionExports', () => {
{
rule: 'testRule1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
],
},
@@ -36,7 +36,7 @@ export type Condition<TRule> = TRule extends PermissionRule<
infer TResourceType,
infer TParams
>
? (...params: TParams) => PermissionCondition<TResourceType, TParams>
? (params: TParams) => PermissionCondition<TResourceType, TParams>
: never;
/**
@@ -23,9 +23,11 @@ describe('createConditionFactory', () => {
name: 'test-rule',
description: 'test-description',
resourceType: 'test-resource',
schema: z.tuple([]),
apply: jest.fn(),
toQuery: jest.fn(),
schema: z.object({
foo: z.string(),
}),
apply: (_resource, _params) => true,
toQuery: _params => ({}),
});
it('returns a function', () => {
@@ -35,10 +37,16 @@ describe('createConditionFactory', () => {
describe('return value', () => {
it('constructs a condition with the rule name and supplied params', () => {
const conditionFactory = createConditionFactory(testRule);
expect(conditionFactory('a', 'b', 1, 2)).toEqual({
expect(
conditionFactory({
foo: 'bar',
}),
).toEqual({
rule: 'test-rule',
resourceType: 'test-resource',
params: ['a', 'b', 1, 2],
params: {
foo: 'bar',
},
});
});
});
@@ -34,10 +34,10 @@ import { PermissionRule } from '../types';
* @public
*/
export const createConditionFactory =
<TResourceType extends string, TParams extends any[]>(
<TResourceType extends string, TParams extends Record<string, unknown>>(
rule: PermissionRule<unknown, unknown, TResourceType, TParams>,
) =>
(...params: TParams): PermissionCondition<TResourceType, TParams> => ({
(params: TParams): PermissionCondition<TResourceType, TParams> => ({
rule: rule.name,
resourceType: rule.resourceType,
params,
@@ -27,22 +27,22 @@ const transformConditions = createConditionTransformer([
name: 'test-rule-1',
description: 'Test rule 1',
resourceType: 'test-resource',
schema: z.tuple([z.string(), z.number()]),
schema: z.object({
foo: z.string(),
bar: z.number(),
}),
apply: jest.fn(),
toQuery: jest.fn(
(firstParam: string, secondParam: number) =>
`test-rule-1:${firstParam}/${secondParam}`,
),
toQuery: jest.fn(({ foo, bar }) => `test-rule-1:${foo}/${bar}`),
}),
createPermissionRule({
name: 'test-rule-2',
description: 'Test rule 2',
resourceType: 'test-resource',
schema: z.tuple([z.object({})]),
schema: z.object({
foo: z.object({}),
}),
apply: jest.fn(),
toQuery: jest.fn(
(firstParam: object) => `test-rule-2:${JSON.stringify(firstParam)}`,
),
toQuery: jest.fn(({ foo }) => `test-rule-2:${JSON.stringify(foo)}`),
}),
]);
@@ -55,7 +55,10 @@ describe('createConditionTransformer', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['abc', 123],
params: {
foo: 'abc',
bar: 123,
},
},
expectedResult: 'test-rule-1:abc/123',
},
@@ -63,7 +66,9 @@ describe('createConditionTransformer', () => {
conditions: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ foo: 0 }],
params: {
foo: { foo: 0 },
},
},
expectedResult: 'test-rule-2:{"foo":0}',
},
@@ -73,9 +78,18 @@ describe('createConditionTransformer', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: {},
},
},
{ rule: 'test-rule-2', resourceType: 'test-resource', params: [{}] },
],
},
expectedResult: {
@@ -88,9 +102,18 @@ describe('createConditionTransformer', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: {
foo: {},
},
},
{ rule: 'test-rule-2', resourceType: 'test-resource', params: [{}] },
],
},
expectedResult: {
@@ -102,7 +125,9 @@ describe('createConditionTransformer', () => {
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
},
expectedResult: {
@@ -117,12 +142,17 @@ describe('createConditionTransformer', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
],
},
@@ -132,12 +162,19 @@ describe('createConditionTransformer', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['b', 2],
params: {
foo: 'b',
bar: 2,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ c: 3 }],
params: {
foo: {
c: 3,
},
},
},
],
},
@@ -165,12 +202,19 @@ describe('createConditionTransformer', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ b: 2 }],
params: {
foo: {
b: 2,
},
},
},
],
},
@@ -180,13 +224,20 @@ describe('createConditionTransformer', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['c', 3],
params: {
foo: 'c',
bar: 3,
},
},
{
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ d: 4 }],
params: {
foo: {
d: 4,
},
},
},
},
],
@@ -53,7 +53,7 @@ const mapConditions = <TQuery>(
throw new InputError(`Parameters to rule are invalid`, result.error);
}
return rule.toQuery(...criteria.params);
return rule.toQuery(criteria.params);
};
/**
@@ -40,21 +40,23 @@ const testRule1 = createPermissionRule({
name: 'test-rule-1',
description: 'Test rule 1',
resourceType: 'test-resource',
schema: z.tuple([
z.string().describe('firstParam'),
z.number().describe('secondParam'),
]),
apply: (_resource: any, _firstParam: string, _secondParam: number) => true,
toQuery: (_firstParam: string, _secondParam: number) => ({}),
schema: z.object({
foo: z.string(),
bar: z.number().describe('bar'),
}),
apply: (_resource: any, _params) => true,
toQuery: _params => ({}),
});
const testRule2 = createPermissionRule({
name: 'test-rule-2',
description: 'Test rule 2',
resourceType: 'test-resource',
schema: z.tuple([z.object({}).describe('firstParam')]),
apply: (_resource: any, _firstParam: object) => false,
toQuery: (_firstParam: object) => ({}),
schema: z.object({
foo: z.object({}).describe('foo'),
}),
apply: (_resource: any, _foo) => false,
toQuery: _foo => ({}),
});
describe('createPermissionIntegrationRouter', () => {
@@ -85,23 +87,35 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['abc', 123],
params: {
foo: 'abc',
bar: 123,
},
},
{
anyOf: [
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: { foo: {} },
},
{ rule: 'test-rule-2', resourceType: 'test-resource', params: [{}] },
],
},
{
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
},
{
@@ -111,12 +125,17 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
],
},
@@ -126,12 +145,17 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['b', 2],
params: {
foo: 'b',
bar: 2,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ c: 3 }],
params: {
foo: { c: 3 },
},
},
],
},
@@ -167,16 +191,25 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ foo: 0 }],
params: {
foo: { foo: 0 },
},
},
{
allOf: [
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: { foo: {} },
},
{ rule: 'test-rule-2', resourceType: 'test-resource', params: [{}] },
],
},
{
@@ -186,12 +219,17 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ b: 2 }],
params: {
foo: { b: 2 },
},
},
],
},
@@ -201,13 +239,18 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['c', 3],
params: {
foo: 'c',
bar: 3,
},
},
{
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{ d: 4 }],
params: {
foo: { d: 4 },
},
},
},
],
@@ -253,7 +296,10 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
},
{
@@ -263,7 +309,9 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
},
{
@@ -274,7 +322,10 @@ describe('createPermissionIntegrationRouter', () => {
not: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
},
},
@@ -286,7 +337,9 @@ describe('createPermissionIntegrationRouter', () => {
not: {
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
},
},
@@ -299,12 +352,17 @@ describe('createPermissionIntegrationRouter', () => {
{
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
{
rule: 'test-rule-2',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
],
},
@@ -348,7 +406,9 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-incorrect-resource-1',
params: [{}],
params: {
foo: {},
},
},
},
{
@@ -358,7 +418,9 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: [{}],
params: {
foo: {},
},
},
},
{
@@ -368,7 +430,9 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-incorrect-resource-2',
params: [{}],
params: {
foo: {},
},
},
},
],
@@ -396,7 +460,7 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: [],
params: {},
},
},
],
@@ -433,7 +497,10 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
},
{
@@ -443,7 +510,10 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
},
{
@@ -453,7 +523,10 @@ describe('createPermissionIntegrationRouter', () => {
conditions: {
rule: 'test-rule-1',
resourceType: 'test-resource',
params: ['a', 1],
params: {
foo: 'a',
bar: 1,
},
},
},
],
@@ -532,21 +605,20 @@ describe('createPermissionIntegrationRouter', () => {
resourceType: testRule1.resourceType,
schema: {
$schema: 'http://json-schema.org/draft-07/schema#',
items: [
{
description: 'firstParam',
additionalProperties: false,
properties: {
foo: {
type: 'string',
},
{
description: 'secondParam',
bar: {
description: 'bar',
type: 'number',
},
],
maxItems: 2,
minItems: 2,
type: 'array',
},
required: ['foo', 'bar'],
type: 'object',
},
parameters: { count: 2 },
parameters: { count: 1 },
},
{
name: testRule2.name,
@@ -554,17 +626,17 @@ describe('createPermissionIntegrationRouter', () => {
resourceType: testRule2.resourceType,
schema: {
$schema: 'http://json-schema.org/draft-07/schema#',
items: [
{
additionalProperties: false,
properties: {
foo: {
additionalProperties: false,
description: 'firstParam',
description: 'foo',
properties: {},
type: 'object',
},
],
maxItems: 1,
minItems: 1,
type: 'array',
},
required: ['foo'],
type: 'object',
},
parameters: { count: 1 },
},
@@ -46,7 +46,7 @@ const permissionCriteriaSchema: z.ZodSchema<
z.object({
rule: z.string(),
resourceType: z.string(),
params: z.array(z.unknown()),
params: z.record(z.unknown()),
}),
]),
);
@@ -132,7 +132,7 @@ const applyConditions = <TResourceType extends string, TResource>(
throw new InputError(`Parameters to rule are invalid`, result.error);
}
return rule.apply(resource, ...criteria.params);
return rule.apply(resource, criteria.params);
};
/**
@@ -25,7 +25,7 @@ export const createPermissionRule = <
TResource,
TQuery,
TResourceType extends string,
TParams extends unknown[],
TParams extends Record<string, unknown>,
>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) => rule;
@@ -40,7 +40,7 @@ export const createPermissionRule = <
*/
export const makeCreatePermissionRule =
<TResource, TQuery, TResourceType extends string>() =>
<TParams extends unknown[]>(
<TParams extends Record<string, unknown>>(
rule: PermissionRule<TResource, TQuery, TResourceType, TParams>,
) =>
createPermissionRule(rule);
@@ -31,7 +31,7 @@ describe('permission integration utils', () => {
name: 'test-rule-1',
description: 'Test rule 1',
resourceType: 'test-resource',
schema: z.tuple([]),
schema: z.object({}),
apply: jest.fn(),
toQuery: jest.fn(),
});
@@ -40,7 +40,7 @@ describe('permission integration utils', () => {
name: 'test-rule-2',
description: 'Test rule 2',
resourceType: 'test-resource',
schema: z.tuple([]),
schema: z.object({}),
apply: jest.fn(),
toQuery: jest.fn(),
});
+8 -4
View File
@@ -37,7 +37,7 @@ export type PermissionRule<
TResource,
TQuery,
TResourceType extends string,
TParams extends unknown[] = unknown[],
TParams extends Record<string, unknown> = Record<string, unknown>,
> = {
name: string;
description: string;
@@ -46,19 +46,23 @@ export type PermissionRule<
/**
* A ZodSchema that documents the parameters that this rule accepts.
*/
schema: z.ZodSchema<TParams>;
schema: z.ZodObject<{
[P in keyof TParams]-?: TParams[P] extends undefined
? z.ZodOptionalType<z.ZodType<TParams[P]>>
: z.ZodType<TParams[P], any, any>;
}>;
/**
* Apply this rule to a resource already loaded from a backing data source. The params are
* arguments supplied for the rule; for example, a rule could be `isOwner` with entityRefs as the
* params.
*/
apply(resource: TResource, ...params: TParams): boolean;
apply(resource: TResource, params: TParams): boolean;
/**
* Translate this rule to criteria suitable for use in querying a backing data store. The criteria
* can be used for loading a collection of resources efficiently with conditional criteria already
* applied.
*/
toQuery(...params: TParams): PermissionCriteria<TQuery>;
toQuery(params: TParams): PermissionCriteria<TQuery>;
};