Skip to content

Commit

Permalink
fix: should be able to reach nullable operand
Browse files Browse the repository at this point in the history
  • Loading branch information
Hrdtr committed Jul 4, 2024
1 parent 7ff7a47 commit c53675c
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 4 deletions.
6 changes: 3 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ type ContextField<
Prefix extends string = "$context."
> = `context.${string}` | {
[K in keyof Context]: K extends string | number
? Context[K] extends Record<string, unknown>
? ContextField<Context[K], T, `${Prefix}${K}.`>
: T extends Context[K] ? `${Prefix}${K}` : never
? NonNullable<Context[K]> extends Record<string, unknown>
? Extract<Context[K], null | undefined> extends never ? ContextField<NonNullable<Context[K]>, T, `${Prefix}${K}.`> : ContextField<NonNullable<Context[K]>, T, `${Prefix}${K}?.`>
: T extends NonNullable<Context[K]> ? `${Prefix}${K}` : never
: never
}[keyof Context];

Expand Down
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const isContextualOperand = (path: unknown): path is string => typeof pat
*/
export const getContextValue = <T extends Record<string, unknown>, U>(context: T, path: string): U | undefined => {
return (path.replace(path.startsWith('$') ? '$context.' : 'context.', ''))
.replaceAll('?.', '.')
.split('.')
// eslint-disable-next-line unicorn/no-array-reduce
.reduce((o, k) => (o || {})[k], (context ?? {}) as Record<string, any>) as U | undefined
Expand Down
55 changes: 54 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,60 @@ describe('Guantr.can', () => {
expect(guantr.can('read', ['user', mockUser])).toBe(false)
})

it('should be able to reach nullable context', () => {
const mockContext1 = {
address: null
} as { address: { line1: string } | null }
const mockContext2 = {
address: { line1: '123 Main St', }
} as { address: { line1: string } | null }

const guantr1 = createGuantr<MockMeta>().withContext(mockContext1)
guantr1.setPermissions([
{
resource: 'user',
action: 'read',
condition: {
address: {
line1: ['equals', '$context.address?.line1']
}
},
inverted: false
},
])

const guantr2 = createGuantr<MockMeta>().withContext(mockContext2)
guantr2.setPermissions([
{
resource: 'user',
action: 'read',
condition: {
address: {
line1: ['equals', '$context.address?.line1']
}
},
inverted: false
},
])

const mockUser: MockResourceMap['user'] = {
id: 1,
name: 'John Doe',
suspended: null,
roles: [{ id: 1, name: 'admin' }, { id: 2, name: 'user' }],
address: {
line1: '123 Main St',
line2: 'Apt 4B',
city: 'AnyTown',
state: 'CA',
zip: '12345',
country: 'ID'
}
}
expect(guantr1.can('read', ['user', mockUser])).toBe(false)
expect(guantr2.can('read', ['user', mockUser])).toBe(true)
})

it('should handle inverted rule correctly', () => {
const guantr = createGuantr<MockMeta>()
guantr.setPermissions([
Expand Down Expand Up @@ -341,4 +395,3 @@ describe('Guantr.can', () => {
expect(guantr.can('read', ['post', { ...post, published: false }])).toBe(false)
})
})

0 comments on commit c53675c

Please sign in to comment.