Skip to content

Commit

Permalink
feat(auth): add graphql types for role and permission
Browse files Browse the repository at this point in the history
  • Loading branch information
bahdcoder committed Sep 30, 2021
1 parent 9ce722b commit 783a9ad
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 19 deletions.
13 changes: 12 additions & 1 deletion examples/typescript/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { cms } from '@tensei/cms'
import { auth } from '@tensei/auth'
import { rest } from '@tensei/rest'
import { graphql } from '@tensei/graphql'
import { mde, markdown } from '@tensei/mde'
import { auth, permission, role } from '@tensei/auth'

import {
tensei,
Expand Down Expand Up @@ -60,6 +60,17 @@ export default tensei()
welcome(),
cms().plugin(),
auth()
.teams()
.roles([
role('Chief Marketer').permissions([
permission('Create Pages'),
permission('Delete Pages'),
permission('Update Pages'),
permission('Link Menu To Pages')
]),
role('Teacher').permissions([permission('Authorize Comments')])
])
.teamPermissions([permission('Create Article')])
.verifyEmails()
.configureTokens({
accessTokenExpiresIn: 60,
Expand Down
49 changes: 43 additions & 6 deletions packages/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ export class Auth implements AuthContract {
array('Roles')
.default([])
.of('string')
.type('[RoleString]')
.arrayRules(
'required',
`in:${[
Expand All @@ -339,12 +340,31 @@ export class Auth implements AuthContract {
.creationRules('required')
.onlyOnForms()
.hideOnUpdateApi(),
array('All Roles').virtual(function (this: any) {
return this.getAllRoles()
}),
array('All Permissions').virtual(function (this: any) {
return this.getAllPermissions()
}),
array('All Roles')
.type('[Role]')
.virtual(function (this: any) {
return this.getAllRoles().map((role: RoleContract) => ({
slug: role.config.slug,
name: role.config.name,
description: role.config.description,
permissions: role.config.permissions.map(permission => ({
name: permission.config.name,
slug: permission.config.slug,
description: permission.config.description
}))
}))
}),
array('All Permissions')
.type('[Permission]')
.virtual(function (this: any) {
return this.getAllPermissions().map(
(permission: PermissionContract) => ({
slug: permission.config.slug,
name: permission.config.name,
description: permission.config.description
})
)
}),
boolean('Blocked')
.nullable()
.default(false)
Expand Down Expand Up @@ -1611,6 +1631,23 @@ export class Auth implements AuthContract {
${camelCaseName}: ${pascalCaseName}!
}
enum RoleString {
${this.config.roles.map(role => role.formatForEnum())}
}
type Permission {
name: String
slug: String
description: String
}
type Role {
name: String
slug: String
description: String
permissions: [Permission]
}
type LoginResponse {
${
cookies
Expand Down
4 changes: 0 additions & 4 deletions packages/auth/src/teams/Permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,6 @@ export class Permission implements PermissionContract {
formatForEnum() {
return this.config.slug.split('-').join('_').toUpperCase()
}

static formatToSlug(slug: string) {
return slug.toLowerCase().split('_').join('-')
}
}

export const permission = (slug: string, description?: string) =>
Expand Down
1 change: 1 addition & 0 deletions packages/auth/src/teams/Teams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export class Teams {
${this.auth.config.teamPermissions.map(permission =>
permission.formatForEnum()
)}
${this.auth.config.teamPermissions.length === 0 ? `NOOB` : ``}
}
type TeamPermission {
Expand Down
1 change: 0 additions & 1 deletion packages/cms/plugin/routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as formatter from 'express-response-formatter'
import { FindOptions, ReferenceType } from '@mikro-orm/core'
import { route, RouteContract, Utils, Config } from '@tensei/common'

Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/fields/Array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Field from './Field'
type ArrayTypes = 'string' | 'number' | 'decimal' | 'date'

export class ArrayField extends Field {
protected arrayOf: ArrayTypes = 'string'
public arrayOf: ArrayTypes = 'string'

public component = {
form: 'Array',
Expand Down
14 changes: 14 additions & 0 deletions packages/common/src/fields/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export class Field implements FieldContract {

public fakerFn = undefined

public graphqlType = ''

public showHideFieldFromApi = {
hideOnCreateApi: false,
hideOnUpdateApi: false,
Expand Down Expand Up @@ -346,6 +348,18 @@ export class Field implements FieldContract {
return this
}

public type(type: string) {
this.graphqlType = type

return this
}

public serializer(serializeFn: (value: any) => any) {
this.property.serializer = serializeFn

return this
}

/**
*
* Hide this field from the detail page
Expand Down
3 changes: 3 additions & 0 deletions packages/common/typings/fields.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,17 @@ declare module '@tensei/common/fields' {
}
fakerFn?: (fn: typeof Faker) => any
property: FieldProperty
graphqlType: string
relatedProperty: FieldProperty
afterConfigSet(): void
type(type: string): this
isRelationshipField: boolean
onUpdate(hook: () => any): this
onCreate(hook: () => any): this
virtual(compute: (value: any) => any): this
removeFromSidebarOnForms(): this
dockToSidebarOnForms(): this
serializer(serializeFn: (value: any) => any): this
formComponent(component: string): this
indexComponent(component: string): this
detailComponent(component: string): this
Expand Down
14 changes: 12 additions & 2 deletions packages/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,21 @@ class Graphql {
}
}

if (
field.property.type === 'json' &&
field.validationRules.includes('array')
) {
FieldType = `[${FieldType}]`
}

if (!field.serialize().isNullable || field.property.primary) {
FieldType = `${FieldType}!`
}

if (field.graphqlType) {
FieldType = field.graphqlType
}

return `
${FieldKey}: ${FieldType}`
}
Expand Down Expand Up @@ -685,8 +696,7 @@ input IdWhereQuery {
app,
graphQlMiddleware,
serverUrl,
resources,
extendEvents
resources
} = config

const typeDefs = [
Expand Down
2 changes: 1 addition & 1 deletion packages/rest/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const authorizeMiddleware = (checks: AuthorizeFunction[]) => {

if (authorized.filter(result => result).length !== checks.length) {
return response.status(401).json({
message: 'Unauthorized.'
message: 'Unauthorized beans.'
})
}

Expand Down
4 changes: 1 addition & 3 deletions packages/tests/packages/auth/roles.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,7 @@ test('can virtually get allRoles and allPermissions', async () => {
})

expect(
customer
.toJSON()
.allRoles.map(role => [role.config.name, role.config.permissions.length])
customer.toJSON().allRoles.map(role => [role.name, role.permissions.length])
).toEqual([
['Writer', 2],
['Regular User', 3]
Expand Down

0 comments on commit 783a9ad

Please sign in to comment.