Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/agency.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Brand, Domains, Projects } from './resources/agency'
import { Brand, Domains, Projects, Roles } from './resources/agency'

import type { Blutui } from './blutui'
import type { GetOptions, PostOptions } from './types'
Expand All @@ -7,6 +7,7 @@ export class Agency {
readonly brand = new Brand(this)
readonly domains = new Domains(this)
readonly projects = new Projects(this)
readonly roles = new Roles(this)

constructor(
public username: string,
Expand Down
2 changes: 1 addition & 1 deletion src/resources/agency/domains/domains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class Domains {
/**
* Update a domain for the current agency.
*
* @param payload - The values to update the brand
* @param payload - The values to update the domain
*/
async update(id: string, payload: UpdateDomainOptions): Promise<Domain> {
const { data } = await this.agency.patch<
Expand Down
1 change: 1 addition & 0 deletions src/resources/agency/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { Brand } from './brand/brand'
export { Domains } from './domains/domains'
export { Projects } from './projects/projects'
export { Roles } from './roles/roles'
22 changes: 22 additions & 0 deletions src/resources/agency/roles/fixtures/role-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"object": "list",
"data": [
{
"id": "9bfdb42b-1bf0-4510-978e-46aa329f8efa",
"object": "role",
"name": "Owner",
"description": "Owner of the domain",
"created_at": 1716170007,
"updated_at": 1716170007
}
],
"meta": {
"hasMore": false,
"currentPage": 1,
"from": 1,
"to": 1,
"perPage": 10,
"total": 1,
"lastPage": 1
}
}
12 changes: 12 additions & 0 deletions src/resources/agency/roles/fixtures/role.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "9bfdb42b-1bf0-4510-978e-46aa329f8efa",
"object": "role",
"name": "Owner",
"description": "Owner of the domain",
"permissions": {
"user.read": true,
"user.write": false
},
"created_at": 1716170007,
"updated_at": 1716170007
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Permission } from './role.interface'

export interface CreateRoleOptions {
name: string
description?: string | null
permissions?: {
[key in Permission]: boolean
}
}

export interface SerializedCreateRoleOptions {
name: string
description?: string | null
permissions?: {
[key in Permission]: boolean
}
}
3 changes: 3 additions & 0 deletions src/resources/agency/roles/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './role.interface'
export * from './create-role-options.interface'
export * from './update-role-options.interface'
42 changes: 42 additions & 0 deletions src/resources/agency/roles/interfaces/role.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
export type Permission =
| 'domain.delete'
| 'domain.read'
| 'domain.write'
| 'organization.delete'
| 'organization.read'
| 'organization.write'
| 'site.delete'
| 'site.export'
| 'site.publish'
| 'site.read'
| 'site.transfer'
| 'site.write'
| 'stripe'
| 'user.admin'
| 'user.delete'
| 'user.invite'
| 'user.read'

export interface Role {
id: string
object: 'role'
name: string
description: string
isSuper: boolean
usersCount?: number
permissions: { [key in Permission]: boolean }
createdAt: number
updatedAt: number
}

export interface RoleResponse {
id: string
object: 'role'
name: string
description: string
is_super: boolean
users_count?: number
permissions: { [key in Permission]: boolean }
created_at: number
updated_at: number
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Permission } from './role.interface'

export interface UpdateRoleOptions {
name?: string
description?: string | null
permissions?: {
[key in Permission]: boolean
}
}

export interface SerializedUpdateRoleOptions {
name?: string
description?: string | null
permissions?: {
[key in Permission]: boolean
}
}
82 changes: 82 additions & 0 deletions src/resources/agency/roles/roles.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import fetch from 'jest-fetch-mock'
import { Blutui } from '@/blutui'
import { fetchOnce, fetchURL } from '@/utils/testing'

import roleFixture from './fixtures/role.json'
import roleListFixture from './fixtures/role-list.json'

const accessToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
const blutui = new Blutui(accessToken)

describe('Role', () => {
beforeEach(() => fetch.resetMocks())

describe('list', () => {
it('can retrieve a list of roles', async () => {
fetchOnce(roleListFixture)
const domains = await blutui.agency('foo').roles.list()

expect(fetchURL()).toBe(`${blutui.baseURL}/v1/agencies/foo/roles`)
expect(domains).toMatchObject({
object: 'list',
})
})
})

describe('get', () => {
it('can retrieve a role information', async () => {
fetchOnce(roleFixture)
const role = await blutui.agency('foo').roles.get(roleFixture.id)

expect(fetchURL()).toBe(
`${blutui.baseURL}/v1/agencies/foo/roles/${roleFixture.id}`
)
expect(role).toMatchObject({
object: 'role',
})
})
})

describe('create', () => {
it('can create a new role', async () => {
fetchOnce(roleFixture)
const role = await blutui.agency('foo').roles.create({
name: 'Owner',
})

expect(fetchURL()).toBe(`${blutui.baseURL}/v1/agencies/foo/roles`)
expect(role).toMatchObject({
object: 'role',
name: 'Owner',
})
})
})

describe('update', () => {
it('can update an agency role', async () => {
fetchOnce(roleFixture)
const role = await blutui
.agency('foo')
.roles.update(roleFixture.id, { name: 'New Role Name' })

expect(fetchURL()).toBe(
`${blutui.baseURL}/v1/agencies/foo/roles/${roleFixture.id}`
)
expect(role).toMatchObject({
object: 'role',
})
})
})

describe('remove', () => {
it('can remove an role', async () => {
fetchOnce(roleFixture)
await blutui.agency('foo').roles.remove(roleFixture.id)

expect(fetchURL()).toBe(
`${blutui.baseURL}/v1/agencies/foo/roles/${roleFixture.id}`
)
})
})
})
82 changes: 82 additions & 0 deletions src/resources/agency/roles/roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import {
deserializeRole,
deserializeRoleList,
serializeCreateRoleOptions,
serializeUpdateRoleOptions,
} from './serializers'

import type { Agency } from '@/agency'
import type {
CreateRoleOptions,
Role,
RoleResponse,
SerializedCreateRoleOptions,
SerializedUpdateRoleOptions,
UpdateRoleOptions,
} from './interfaces'
import type {
DeletedResponse,
List,
ListResponse,
PaginationOptions,
} from '@/types'

export class Roles {
constructor(private readonly agency: Agency) {}

/**
* Get a list of roles for the current agency.
*/
async list(options?: PaginationOptions): Promise<List<Role>> {
const { data } = await this.agency.get<ListResponse<RoleResponse>>(
'roles',
{ query: options }
)

return deserializeRoleList(data)
}

/**
* Get a role's information by ID.
*/
async get(id: string): Promise<Role> {
const { data } = await this.agency.get<RoleResponse>(`roles/${id}`)

return deserializeRole(data)
}

/**
* Add a role to your agency.
*/
async create(payload: CreateRoleOptions): Promise<Role> {
const { data } = await this.agency.post<
RoleResponse,
SerializedCreateRoleOptions
>('roles', serializeCreateRoleOptions(payload))

return deserializeRole(data)
}

/**
* Update a role in the current agency.
*
* @param payload - The values to update the role
*/
async update(id: string, payload: UpdateRoleOptions): Promise<Role> {
const { data } = await this.agency.patch<
RoleResponse,
SerializedUpdateRoleOptions
>(`roles/${id}`, serializeUpdateRoleOptions(payload))

return deserializeRole(data)
}

/**
* Remove a role from the current agency.
*/
async remove(id: string): Promise<DeletedResponse> {
const { data } = await this.agency.delete<DeletedResponse>(`roles/${id}`)

return data
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type {
CreateRoleOptions,
SerializedCreateRoleOptions,
} from '../interfaces'

export const serializeCreateRoleOptions = (
options: CreateRoleOptions
): SerializedCreateRoleOptions => ({
name: options.name,
description: options.description,
permissions: options.permissions,
})
3 changes: 3 additions & 0 deletions src/resources/agency/roles/serializers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './role.serializer'
export * from './create-role-options.serializer'
export * from './update-role-options.serializer'
38 changes: 38 additions & 0 deletions src/resources/agency/roles/serializers/role.serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { deserializePaginationMeta } from '@/utils/serializers'

import type { List, ListResponse } from '@/types'
import type { Role, RoleResponse } from '../interfaces'

export const deserializeRole = (role: RoleResponse): Role => {
const {
id,
object,
name,
description,
is_super: isSuper,
permissions,
created_at: createdAt,
updated_at: updatedAt,
users_count: usersCount,
} = role

return {
id,
object,
name,
description,
isSuper,
permissions,
createdAt,
updatedAt,
...(usersCount !== undefined && { usersCount }),
}
}

export const deserializeRoleList = (
roles: ListResponse<RoleResponse>
): List<Role> => ({
object: 'list',
data: roles.data.map(deserializeRole),
meta: deserializePaginationMeta(roles.meta),
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type {
SerializedUpdateRoleOptions,
UpdateRoleOptions,
} from '../interfaces'

export const serializeUpdateRoleOptions = (
options: UpdateRoleOptions
): SerializedUpdateRoleOptions => ({
name: options.name,
description: options.description,
permissions: options.permissions,
})