Skip to content

Commit

Permalink
Add actionsFromClass method for use in extended Policy classes
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisalley committed Jul 31, 2023
1 parent 7dab84b commit 37ffbb0
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,16 @@ export default class Policy {
})
return newPolicy
}

actionsFromClass(): void {
const actionNames = Object.getOwnPropertyNames(
this.constructor.prototype
).filter((methodName) => methodName !== 'constructor')
this.actions = new Map(
actionNames.map((actionName) => [
actionName,
(): boolean => this[actionName](this.user, this.record),
])
)
}
}
68 changes: 68 additions & 0 deletions test/policy.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,71 @@ describe('copy function', () => {
expect(copiedPolicy.actions.get(actionName)).toEqual(actionFunction)
})
})

describe('actionsFromClass function', () => {
type AuthorisableUser = { isAdmin: boolean }
type AuthorisableRecord = { draft: boolean }

class PostPolicy extends Policy {
user: AuthorisableUser

record: AuthorisableRecord

constructor(
user: AuthorisableUser | undefined,
record: AuthorisableRecord | undefined
) {
super(user, record)
this.actionsFromClass.apply(this)
}

// eslint-disable-next-line class-methods-use-this
view(): boolean {
return true
}

// eslint-disable-next-line class-methods-use-this
edit(): boolean {
if (this.user?.isAdmin || this.record?.draft) {
return true
}
return false
}
}

it('does not turn the policy class constructor into an action', () => {
const postPolicy = new PostPolicy(undefined, undefined)
expect(postPolicy.actions.has('constructor')).toBe(false)
})

it('copies the correct number of actions into the actions map', () => {
const postPolicy = new PostPolicy(undefined, undefined)
expect(postPolicy.actions.size).toEqual(2)
})

it('copies the action names defined in the class into the actions map', () => {
const postPolicy = new PostPolicy(undefined, undefined)
expect(postPolicy.actions.has('view')).toBe(true)
expect(postPolicy.actions.has('edit')).toBe(true)
})

it('copies the action methods defined in the class into the actions map', () => {
const postPolicy = new PostPolicy(undefined, undefined)
expect(postPolicy.can('view')).toBe(true)
expect(postPolicy.can('edit')).toBe(false)
})

it('copies the user property of the class instance into the actions map', () => {
const admin = { isAdmin: true }
expect(new PostPolicy(admin, undefined).can('edit')).toBe(true)
const nonAdmin = { isAdmin: false }
expect(new PostPolicy(nonAdmin, undefined).can('edit')).toBe(false)
})

it('copies the record property of the class instance into the actions map', () => {
const draftPost = { draft: true }
expect(new PostPolicy(undefined, draftPost).can('edit')).toBe(true)
const publishedPost = { draft: false }
expect(new PostPolicy(undefined, publishedPost).can('edit')).toBe(false)
})
})

0 comments on commit 37ffbb0

Please sign in to comment.