prisma-extension-casl
Version:
Enforce casl abilities on prisma client
378 lines (350 loc) • 17.3 kB
text/typescript
import { applyDataQuery } from '../src/applyDataQuery'
import { abilityBuilder } from './abilities'
describe('apply data query', () => {
it('converts id to connect', () => {
const { can, build } = abilityBuilder()
can('update', 'Post')
can('update', 'User')
const result = applyDataQuery(build(), { data: { authorId: 0 }, where: { id: 0 } }, 'update', 'update', 'Post')
expect(result.args).toEqual({ data: { author: { connect: { id: 0, } } }, where: { id: 0, } })
expect(result.creationTree).toEqual({ children: { author: { mutation: [], children: {}, model: 'User', action: "update" } }, model: 'Post', action: "update", mutation: [{ fields: ['authorId'], where: { id: 0 } }], })
})
it('throws error if update on connection is not allowed', () => {
const { can, cannot, build } = abilityBuilder()
can('update', 'Post')
cannot('update', 'User')
expect(() => applyDataQuery(build(), { data: { authorId: 0 }, where: { id: 0 } }, 'update', 'update', 'Post')).toThrow(`It's not allowed to "update" "id" on "User"`)
})
; (['update', 'create'] as const).map((mutation) => {
describe(mutation, () => {
it('adds where clause to query', () => {
const { can, build } = abilityBuilder()
can(mutation, 'User', {
id: 0
})
const result = applyDataQuery(build(), { data: { id: 0 }, where: { id: 1 } }, mutation, mutation, 'User')
expect(result.args).toEqual({
data: { id: 0 },
where: { id: 1, AND: [{ OR: [{ id: 0 }] }] }
})
expect(result.creationTree).toEqual({ children: {}, model: 'User', action: mutation, mutation: [{ fields: ['id'], where: { id: 1 } }], })
})
it('throws error if mutation of property is omitted', () => {
const { can, cannot, build } = abilityBuilder()
can(mutation, 'User')
cannot(mutation, 'User', 'email')
expect(() => applyDataQuery(build(), {
data: { email: '-1' }, where: {
id: 0
}
}, mutation, mutation, 'User')).toThrow(`It's not allowed to "${mutation}" "email" on "User"`)
})
it('throws error if mutation of property is not permitted', () => {
const { can, build } = abilityBuilder()
can(mutation, 'User', 'id')
expect(() => applyDataQuery(build(), {
data: { email: '-1' }, where: {
id: 0
}
}, mutation, mutation, 'User')).toThrow(`It's not allowed to run "${mutation}" on "User"`)
})
})
})
describe('upsert', () => {
it('adds where clause to query', () => {
const { can, build } = abilityBuilder()
can('update', 'Thread')
can('update', 'User', { id: 0 })
can('create', 'User')
const result = applyDataQuery(build(), { data: { creator: { upsert: { create: { email: '-1' }, update: { email: '-1' }, where: { id: 1 } } } }, where: { id: 0 } }, 'update', 'update', 'Thread')
expect(result.args).toEqual({ data: { creator: { upsert: { create: { email: '-1' }, update: { email: '-1' }, where: { id: 1, AND: [{ OR: [{ id: 0 }] }] } } } }, where: { id: 0, } })
expect(result.creationTree).toEqual({
action: 'update',
model: 'Thread',
children: {
creator: {
children: {},
action: 'create',
model: 'User',
mutation: [{ fields: ['email'], where: { id: 1 } }],
}
},
mutation: [{ fields: [], where: { id: 0 } }],
})
})
it('throws error if creation is not allowed', () => {
const { can, cannot, build } = abilityBuilder()
can('update', 'Thread')
can('update', 'User', { id: 0 })
can('create', 'User')
cannot('create', 'User', 'email')
expect(() => applyDataQuery(build(), { data: { creator: { upsert: { create: { email: '-1' }, update: { email: '-1' }, where: { id: 1 } } } }, where: { id: 0 } }, 'update', 'update', 'Thread'))
.toThrow(`It's not allowed to "create" "email" on "User"`)
})
})
describe('connect', () => {
it('adds where and connection clause in nested connection update', () => {
const { can, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
can('update', 'Post', {
id: 1
})
const result = applyDataQuery(build(), { data: { id: 1, posts: { connect: { id: 0 } } }, where: { id: 0 } }, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { connect: { id: 0, AND: [{ OR: [{ id: 1 }] }] } } }, where: { id: 0, AND: [{ OR: [{ id: 0 }] }] } })
expect(result.creationTree).toEqual({ children: { posts: { children: {}, action: 'update', model: 'Post', mutation: [] } }, model: 'User', action: "update", mutation: [{ fields: ['id'], where: { id: 0 } }], })
})
it('adds where and connection clause in nested array connection update', () => {
const { can, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
can('update', 'Post', {
id: 1
})
const result = applyDataQuery(build(), { data: { id: 1, posts: { connect: [{ id: 0 }] } }, where: { id: 0 } }, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { connect: [{ id: 0, AND: [{ OR: [{ id: 1 }] }] }] } }, where: { id: 0, AND: [{ OR: [{ id: 0 }] }] } })
expect(result.creationTree).toEqual({ children: { posts: { children: {}, model: 'Post', action: "update", mutation: [] } }, model: 'User', action: "update", mutation: [{ fields: ['id'], where: { id: 0 } }] })
})
it('throws error if data in nested connection is not allowed', () => {
const { can, cannot, build } = abilityBuilder()
can('update', 'User')
cannot('update', 'Post')
expect(() => applyDataQuery(build(), { data: { id: 1, posts: { connect: { id: 0 } } }, where: { id: 0 } }, 'update', 'update', 'User'))
.toThrow(`It's not allowed to "update" "id" on "Post"`)
})
})
describe('disconnect', () => {
it('accepts disconnect: true', () => {
const { can, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
can('update', 'Post', {
id: 1
})
const result = applyDataQuery(build(), { data: { id: 1, posts: { disconnect: true } }, where: { id: 0 } }, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { disconnect: true } }, where: { id: 0, AND: [{ OR: [{ id: 0 }] }] } })
expect(result.creationTree).toEqual({ children: { posts: { children: {}, action: 'update', model: 'Post', mutation: [] } }, model: 'User', action: "update", mutation: [{ fields: ['id'], where: { id: 0 } }] })
})
})
describe('connectOrCreate', () => {
it('adds where and connection clause in nested connection update', () => {
const { can, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
can('create', 'Post', {
text: ''
})
can('update', 'Post', {
id: 2
})
const result = applyDataQuery(build(), {
data: {
id: 1, posts: {
connectOrCreate: {
create: { text: '' },
where: {
id: 1
}
}
}
},
where: {
id: 0
}
}, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { connectOrCreate: { create: { text: '' }, where: { id: 1, AND: [{ OR: [{ id: 2 }] }] } } } }, where: { id: 0, AND: [{ OR: [{ id: 0 }] }] } })
expect(result.creationTree).toEqual({ action: 'update', model: 'User', children: { posts: { model: 'Post', action: 'create', children: {}, mutation: [{ fields: ['text'], where: { id: 1 } }], } }, mutation: [{ fields: ['id'], where: { id: 0 } }], })
})
it('adds where and connection clause in nested array connection update', () => {
const { can, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
can('create', 'Post')
can('update', 'Post', {
id: 2
})
const result = applyDataQuery(build(), {
data: {
id: 1, posts: {
connectOrCreate: [{
create: { text: '' },
where: {
id: 0
}
}]
}
},
where: {
id: 0
}
}, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { connectOrCreate: [{ create: { text: '' }, where: { id: 0, AND: [{ OR: [{ id: 2 }] }] } }] } }, where: { id: 0, AND: [{ OR: [{ id: 0 }] }] } })
expect(result.creationTree).toEqual({ action: 'update', model: 'User', children: { posts: { model: 'Post', action: 'create', children: {}, mutation: [{ fields: ['text'], where: { id: 0 } }] } }, mutation: [{ fields: ['id'], where: { id: 0 } }] })
})
it('throws error if data in nested connection is not allowed', () => {
const { can, cannot, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
cannot('create', 'Post', {
id: 1
})
can('update', 'Post', {
id: 2
})
expect(() => applyDataQuery(build(), {
data: {
id: 1, posts: {
connectOrCreate: [{
create: { text: '' },
where: {
id: 0
}
}]
}
},
where: {
id: 0
}
}, 'update', 'update', 'User'))
.toThrow(`It's not allowed to "create" "text" on "Post"`)
})
it('throws error if data in nested create property in connection is not allowed', () => {
const { can, build } = abilityBuilder()
can('update', 'User', {
id: 0
})
can('create', 'Post', 'id', {
id: 1
})
can('update', 'Post', {
id: 2
})
expect(() => applyDataQuery(build(), {
data: {
id: 1, posts: {
connectOrCreate: [{
create: { text: '' },
where: {
id: 0
}
}]
}
},
where: {
id: 0
}
}, 'update', 'update', 'User'))
.toThrow(`It's not allowed to "create" "text" on "Post"`)
})
})
describe('nested update', () => {
it('can update nested nested update', () => {
const { can, build } = abilityBuilder()
can('update', 'User')
can('update', 'Post')
can('update', 'Thread')
const result = applyDataQuery(build(), { data: { id: 1, posts: { update: { data: { thread: { update: { id: 0 } } }, where: { id: 0 } } } }, where: { id: 0 } }, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { update: { data: { thread: { update: { id: 0 } } }, where: { id: 0, } } } }, where: { id: 0, } })
expect(result.creationTree).toEqual({ children: { posts: { children: { thread: { children: {}, model: 'Thread', action: "update", mutation: [] } }, model: 'Post', action: "update", mutation: [{ fields: [], where: { id: 0 } }] } }, model: 'User', action: "update", mutation: [{ fields: ['id'], where: { id: 0 } }] })
})
it('throws error if data in nested nested update is not allowed', () => {
const { can, cannot, build } = abilityBuilder()
can('update', 'User')
can('update', 'Post')
cannot('update', 'Thread')
expect(() => applyDataQuery(build(), { data: { id: 1, posts: { update: { data: { thread: { update: { id: 0 } } }, where: { id: 0 } } } }, where: { id: 0 } }, 'update', 'update', 'User'))
.toThrow(`It's not allowed to "update" "id" on "Thread"`)
})
})
describe('nested delete', () => {
it('can update nested nested delete', () => {
const { can, build } = abilityBuilder()
can('update', 'User')
can('delete', 'Post')
const result = applyDataQuery(build(), { data: { id: 1, posts: { delete: { id: 0 } } }, where: { id: 0 } }, 'update', 'update', 'User')
expect(result.args).toEqual({ data: { id: 1, posts: { delete: { id: 0 } } }, where: { id: 0, } })
expect(result.creationTree).toEqual({
action: "update",
children: {
posts: {
action: "delete",
children: {},
model: "Post",
mutation: [],
},
},
model: "User",
mutation: [
{
fields: [
"id",
],
where: {
id: 0,
},
},
],
})
})
// it('throws error if data in nested nested update is not allowed', () => {
// const { can, cannot, build } = abilityBuilder()
// can('update', 'User')
// can('update', 'Post')
// cannot('update', 'Thread')
// expect(() => applyDataQuery(build(), { data: { id: 1, posts: { update: { data: { thread: { update: { id: 0 } } }, where: { id: 0 } } } }, where: { id: 0 } }, 'update', 'User'))
// .toThrow(`It's not allowed to "update" "id" on "Thread"`)
// })
})
describe('createMany', () => {
it('adds where and connection clause in nested connection update', () => {
const { can, build } = abilityBuilder()
can('create', 'User')
can('create', 'Post')
const result = applyDataQuery(build(), {
data: {
id: 0,
posts: {
createMany: {
data: {
text: ''
}
}
}
}
}, 'create', 'create', 'User')
expect(result.args)
.toEqual({ data: { id: 0, posts: { createMany: { data: { text: '' } } } } })
expect(result.creationTree).toEqual({ action: 'create', model: 'User', children: { posts: { model: 'Post', action: 'create', children: {}, mutation: [] } }, mutation: [] })
})
it('throws error if data in nested create is not allowed', () => {
const { can, cannot, build } = abilityBuilder()
can('create', 'User')
can('create', 'Post')
cannot('create', 'Post', 'text')
expect(() => applyDataQuery(build(), {
data: {
email: '-',
posts: {
createMany: {
data: [
{
id: 0
},
{
text: ''
}
]
}
}
}
}, 'create', 'create', 'User'))
.toThrow(`It's not allowed to "create" "text" on "Post"`)
})
})
})