From cee0b4af93686a83992922e118650fb02501e72c Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Tue, 9 Jul 2024 14:45:39 +1200 Subject: [PATCH 01/12] feat: Add Projects resource to Console API --- src/agency.ts | 3 +- src/resources/agency/domains/domains.ts | 4 +-- src/resources/agency/index.ts | 1 + .../agency/project/interfaces/index.ts | 0 .../project/interfaces/project.interface.ts | 13 ------- .../agency/projects/interfaces/index.ts | 1 + .../projects/interfaces/project.interface.ts | 35 +++++++++++++++++++ src/resources/agency/projects/projects.ts | 16 +++++++++ .../agency/projects/serializers/index.ts | 1 + .../serializers/project.serializer.ts | 29 +++++++++++++++ 10 files changed, 87 insertions(+), 16 deletions(-) delete mode 100644 src/resources/agency/project/interfaces/index.ts delete mode 100644 src/resources/agency/project/interfaces/project.interface.ts create mode 100644 src/resources/agency/projects/interfaces/index.ts create mode 100644 src/resources/agency/projects/interfaces/project.interface.ts create mode 100644 src/resources/agency/projects/projects.ts create mode 100644 src/resources/agency/projects/serializers/index.ts create mode 100644 src/resources/agency/projects/serializers/project.serializer.ts diff --git a/src/agency.ts b/src/agency.ts index 801980e..e69f61d 100644 --- a/src/agency.ts +++ b/src/agency.ts @@ -1,11 +1,12 @@ import { Blutui } from './blutui' -import { Brand, Domains } from './resources/agency' +import { Brand, Domains, Projects } from './resources/agency' import type { GetOptions, PostOptions } from './types' export class Agency { readonly brand = new Brand(this) readonly domains = new Domains(this) + readonly projects = new Projects(this) constructor( public username: string, diff --git a/src/resources/agency/domains/domains.ts b/src/resources/agency/domains/domains.ts index e3c0253..684e06b 100644 --- a/src/resources/agency/domains/domains.ts +++ b/src/resources/agency/domains/domains.ts @@ -1,5 +1,5 @@ import { Agency } from '@/agency' -import { +import type { CreateDomainOptions, DomainResponse, DomainVerifyResponse, @@ -7,7 +7,7 @@ import { SerializedCreateDomainOptions, SerializedUpdateDomainOptions, UpdateDomainOptions, - type Domain, + Domain, } from './interfaces' import { deserializeDomain, diff --git a/src/resources/agency/index.ts b/src/resources/agency/index.ts index d4f10ac..8b83505 100644 --- a/src/resources/agency/index.ts +++ b/src/resources/agency/index.ts @@ -1,2 +1,3 @@ export { Brand } from './brand/brand' export { Domains } from './domains/domains' +export { Projects } from './projects/projects' diff --git a/src/resources/agency/project/interfaces/index.ts b/src/resources/agency/project/interfaces/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/resources/agency/project/interfaces/project.interface.ts b/src/resources/agency/project/interfaces/project.interface.ts deleted file mode 100644 index 7b43950..0000000 --- a/src/resources/agency/project/interfaces/project.interface.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface Project { - id: string - object: 'project' - name: string - description: string -} - -export interface ProjectResponse { - id: string - object: 'project' - name: string - description: string -} diff --git a/src/resources/agency/projects/interfaces/index.ts b/src/resources/agency/projects/interfaces/index.ts new file mode 100644 index 0000000..dbb571f --- /dev/null +++ b/src/resources/agency/projects/interfaces/index.ts @@ -0,0 +1 @@ +export * from './project.interface' diff --git a/src/resources/agency/projects/interfaces/project.interface.ts b/src/resources/agency/projects/interfaces/project.interface.ts new file mode 100644 index 0000000..2f36834 --- /dev/null +++ b/src/resources/agency/projects/interfaces/project.interface.ts @@ -0,0 +1,35 @@ +export interface Project { + id: string + object: 'project' + name: string + description: string | null + image: string | null + handle: string + password: string + timezone: string + subdomain: string + primaryDomain: string | Record + published: boolean + processed: boolean + createdAt: number + updatedAt: number + deletedAt?: number +} + +export interface ProjectResponse { + id: string + object: 'project' + name: string + description: string | null + image: string | null + handle: string + password: string + timezone: string + subdomain: string + primary_domain: string | Record + published: boolean + processed: boolean + created_at: number + updated_at: number + deleted_at?: number +} diff --git a/src/resources/agency/projects/projects.ts b/src/resources/agency/projects/projects.ts new file mode 100644 index 0000000..8e37b75 --- /dev/null +++ b/src/resources/agency/projects/projects.ts @@ -0,0 +1,16 @@ +import { Agency } from '@/agency' +import { Project } from './interfaces' +import { deserializeProjectList } from './serializers' +import { List } from '@/types' + +export class Projects { + constructor(private readonly agency: Agency) {} + + /** + * Get the projects list for the current agency. + */ + async list(): Promise> { + const { data } = await this.agency.get('projects') + return deserializeProjectList(data) + } +} diff --git a/src/resources/agency/projects/serializers/index.ts b/src/resources/agency/projects/serializers/index.ts new file mode 100644 index 0000000..75cddb7 --- /dev/null +++ b/src/resources/agency/projects/serializers/index.ts @@ -0,0 +1 @@ +export * from './project.serializer' diff --git a/src/resources/agency/projects/serializers/project.serializer.ts b/src/resources/agency/projects/serializers/project.serializer.ts new file mode 100644 index 0000000..689aa04 --- /dev/null +++ b/src/resources/agency/projects/serializers/project.serializer.ts @@ -0,0 +1,29 @@ +import { List, ListResponse } from '@/types' +import { Project, ProjectResponse } from '../interfaces' +import { deserializePaginationMeta } from '@/utils/serializers' + +export const deserializeProject = (project: ProjectResponse): Project => ({ + id: project.id, + object: project.object, + name: project.name, + description: project.description, + image: project.image, + handle: project.handle, + password: project.password, + timezone: project.timezone, + subdomain: project.subdomain, + primaryDomain: project.primary_domain, + published: project.published, + processed: project.processed, + createdAt: project.created_at, + updatedAt: project.updated_at, + deletedAt: project.deleted_at, +}) + +export const deserializeProjectList = ( + projects: ListResponse +): List => ({ + object: 'list', + data: projects.data.map(deserializeProject), + meta: deserializePaginationMeta(projects.meta), +}) From 9d3f96af9069af3f9c030a9e34c09c509b0df1f0 Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Thu, 11 Jul 2024 13:43:47 +1200 Subject: [PATCH 02/12] feat: Add expandable type --- src/resources/agency/domains/domains.spec.ts | 8 ++-- src/resources/agency/domains/domains.ts | 15 +++++-- .../{domainList.json => domain-list.json} | 0 .../projects/fixtures/project-list.json | 17 ++++++++ .../agency/projects/fixtures/project.json | 3 ++ .../agency/projects/projects.spec.ts | 40 +++++++++++++++++++ src/resources/agency/projects/projects.ts | 11 +++-- src/types.ts | 4 ++ 8 files changed, 88 insertions(+), 10 deletions(-) rename src/resources/agency/domains/fixtures/{domainList.json => domain-list.json} (100%) create mode 100644 src/resources/agency/projects/fixtures/project-list.json create mode 100644 src/resources/agency/projects/fixtures/project.json create mode 100644 src/resources/agency/projects/projects.spec.ts diff --git a/src/resources/agency/domains/domains.spec.ts b/src/resources/agency/domains/domains.spec.ts index 082fc39..40c0cde 100644 --- a/src/resources/agency/domains/domains.spec.ts +++ b/src/resources/agency/domains/domains.spec.ts @@ -2,7 +2,7 @@ import fetch from 'jest-fetch-mock' import { Blutui } from '@/blutui' import { fetchOnce, fetchURL } from '@/utils/testing' import domainFixture from './fixtures/domain.json' -import domainListFixture from './fixtures/domainList.json' +import domainListFixture from './fixtures/domain-list.json' const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' @@ -74,7 +74,9 @@ describe('Domain', () => { .agency('foo') .domains.update(domainFixture.id, { project: 'project-uuid' }) - expect(fetchURL()).toBe(blutui.baseURL + `/v1/agencies/foo/domains/${domainFixture.id}`) + expect(fetchURL()).toBe( + blutui.baseURL + `/v1/agencies/foo/domains/${domainFixture.id}` + ) expect(domain).toMatchObject({ object: 'domain', }) @@ -117,7 +119,7 @@ describe('Domain', () => { describe('search', () => { it('Search for domains in your agency.', async () => { fetchOnce(domainListFixture) - await blutui.agency('foo').domains.search({"name": "example.com"}) + await blutui.agency('foo').domains.search({ name: 'example.com' }) expect(fetchURL()).toBe( blutui.baseURL + `/v1/agencies/foo/domains/search` diff --git a/src/resources/agency/domains/domains.ts b/src/resources/agency/domains/domains.ts index 684e06b..c80b045 100644 --- a/src/resources/agency/domains/domains.ts +++ b/src/resources/agency/domains/domains.ts @@ -15,7 +15,13 @@ import { serializeCreateDomainOptions, serializeUpdateDomainOptions, } from './serializers' -import { DeletedResponse, List, ListResponse, PaginationOptions } from '@/types' +import { + DeletedResponse, + Expandable, + List, + ListResponse, + PaginationOptions, +} from '@/types' export class Domains { constructor(private readonly agency: Agency) {} @@ -23,18 +29,19 @@ export class Domains { /** * Get the domains list for the current agency. */ - async list(paginationOptions?: PaginationOptions): Promise> { + async list(options?: PaginationOptions): Promise> { const { data } = await this.agency.get>( 'domains', - { query: paginationOptions } + { query: options } ) + return deserializeDomainList(data) } /** * Get a domain information by id. */ - async get(id: string, options?: { expand: string[] }): Promise { + async get(id: string, options?: Expandable<'project'>): Promise { const { data } = await this.agency.get(`domains/${id}`, { query: options, }) diff --git a/src/resources/agency/domains/fixtures/domainList.json b/src/resources/agency/domains/fixtures/domain-list.json similarity index 100% rename from src/resources/agency/domains/fixtures/domainList.json rename to src/resources/agency/domains/fixtures/domain-list.json diff --git a/src/resources/agency/projects/fixtures/project-list.json b/src/resources/agency/projects/fixtures/project-list.json new file mode 100644 index 0000000..b200003 --- /dev/null +++ b/src/resources/agency/projects/fixtures/project-list.json @@ -0,0 +1,17 @@ +{ + "object": "list", + "data": [ + { + "id": "" + } + ], + "meta": { + "hasMore": false, + "currentPage": 1, + "from": 1, + "to": 1, + "perPage": 10, + "total": 1, + "lastPage": 1 + } +} diff --git a/src/resources/agency/projects/fixtures/project.json b/src/resources/agency/projects/fixtures/project.json new file mode 100644 index 0000000..11e3ccd --- /dev/null +++ b/src/resources/agency/projects/fixtures/project.json @@ -0,0 +1,3 @@ +{ + "id": "" +} diff --git a/src/resources/agency/projects/projects.spec.ts b/src/resources/agency/projects/projects.spec.ts new file mode 100644 index 0000000..91cbdb0 --- /dev/null +++ b/src/resources/agency/projects/projects.spec.ts @@ -0,0 +1,40 @@ +import fetch from 'jest-fetch-mock' +import { Blutui } from '@/blutui' +import { fetchOnce, fetchURL } from '@/utils/testing' + +// import projectFixture from './fixtures/project.json' +import projectListFixture from './fixtures/project-list.json' + +const accessToken = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' +const blutui = new Blutui(accessToken) + +describe('Project', () => { + beforeEach(() => fetch.resetMocks()) + + describe('list', () => { + it('can retrieve a list of projects', async () => { + fetchOnce(projectListFixture) + const projects = await blutui.agency('foo').projects.list() + + expect(fetchURL()).toBe(blutui.baseURL + `/v1/agencies/foo/projects`) + expect(projects).toMatchObject({ + object: 'list', + }) + }) + + it('can retrieve a list of projects with an expanded primary domain', async () => { + fetchOnce(projectListFixture) + const projects = await blutui + .agency('foo') + .projects.list({ expand: ['primary_domain'] }) + + expect(fetchURL()).toBe( + blutui.baseURL + `/v1/agencies/foo/projects?expand%5B%5D=primary_domain` + ) + expect(projects).toMatchObject({ + object: 'list', + }) + }) + }) +}) diff --git a/src/resources/agency/projects/projects.ts b/src/resources/agency/projects/projects.ts index 8e37b75..1f1dcf3 100644 --- a/src/resources/agency/projects/projects.ts +++ b/src/resources/agency/projects/projects.ts @@ -1,7 +1,7 @@ import { Agency } from '@/agency' import { Project } from './interfaces' import { deserializeProjectList } from './serializers' -import { List } from '@/types' +import { Expandable, List, PaginationOptions } from '@/types' export class Projects { constructor(private readonly agency: Agency) {} @@ -9,8 +9,13 @@ export class Projects { /** * Get the projects list for the current agency. */ - async list(): Promise> { - const { data } = await this.agency.get('projects') + async list( + options?: PaginationOptions & Expandable<'primary_domain'> + ): Promise> { + const { data } = await this.agency.get('projects', { + query: options, + }) + return deserializeProjectList(data) } } diff --git a/src/types.ts b/src/types.ts index 5d81eb0..3f7e166 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,6 +41,10 @@ export interface PaginationOptions { page?: number } +// Expand resources + +export type Expandable = { expand?: Array } + // Request Options export interface GetOptions { From 688519ca045be93fe2c93153c773dce27405b586 Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Fri, 12 Jul 2024 15:10:24 +1200 Subject: [PATCH 03/12] add additional methods --- src/resources/agency/domains/domains.ts | 2 +- .../create-project-options.interface.ts | 15 ++++++ .../agency/projects/interfaces/index.ts | 2 + .../update-project-options.interface.ts | 15 ++++++ src/resources/agency/projects/projects.ts | 53 ++++++++++++++++++- .../create-project-options.serializer.ts | 14 +++++ .../agency/projects/serializers/index.ts | 2 + .../update-project-options.serializer.ts | 14 +++++ 8 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 src/resources/agency/projects/interfaces/create-project-options.interface.ts create mode 100644 src/resources/agency/projects/interfaces/update-project-options.interface.ts create mode 100644 src/resources/agency/projects/serializers/create-project-options.serializer.ts create mode 100644 src/resources/agency/projects/serializers/update-project-options.serializer.ts diff --git a/src/resources/agency/domains/domains.ts b/src/resources/agency/domains/domains.ts index c80b045..19ddc81 100644 --- a/src/resources/agency/domains/domains.ts +++ b/src/resources/agency/domains/domains.ts @@ -39,7 +39,7 @@ export class Domains { } /** - * Get a domain information by id. + * Get a domain's information by ID. */ async get(id: string, options?: Expandable<'project'>): Promise { const { data } = await this.agency.get(`domains/${id}`, { diff --git a/src/resources/agency/projects/interfaces/create-project-options.interface.ts b/src/resources/agency/projects/interfaces/create-project-options.interface.ts new file mode 100644 index 0000000..971199a --- /dev/null +++ b/src/resources/agency/projects/interfaces/create-project-options.interface.ts @@ -0,0 +1,15 @@ +export interface CreateProjectOptions { + name: string + handle?: string + timezone?: string + description?: string | null + subdomain?: string +} + +export interface SerializedCreateProjectOptions { + name: string + handle?: string + timezone?: string + description?: string | null + subdomain?: string +} diff --git a/src/resources/agency/projects/interfaces/index.ts b/src/resources/agency/projects/interfaces/index.ts index dbb571f..a0d2208 100644 --- a/src/resources/agency/projects/interfaces/index.ts +++ b/src/resources/agency/projects/interfaces/index.ts @@ -1 +1,3 @@ +export * from './create-project-options.interface' export * from './project.interface' +export * from './update-project-options.interface' diff --git a/src/resources/agency/projects/interfaces/update-project-options.interface.ts b/src/resources/agency/projects/interfaces/update-project-options.interface.ts new file mode 100644 index 0000000..82835b2 --- /dev/null +++ b/src/resources/agency/projects/interfaces/update-project-options.interface.ts @@ -0,0 +1,15 @@ +export interface UpdateProjectOptions { + name?: string + description?: string | null + password?: string + timezone?: string + primaryDomain?: string | null +} + +export interface SerializedUpdateProjectOptions { + name?: string + description?: string | null + password?: string + timezone?: string + primary_domain?: string | null +} diff --git a/src/resources/agency/projects/projects.ts b/src/resources/agency/projects/projects.ts index 1f1dcf3..34d8822 100644 --- a/src/resources/agency/projects/projects.ts +++ b/src/resources/agency/projects/projects.ts @@ -1,6 +1,17 @@ import { Agency } from '@/agency' -import { Project } from './interfaces' -import { deserializeProjectList } from './serializers' +import { + CreateProjectOptions, + Project, + ProjectResponse, + SerializedCreateProjectOptions, + SerializedUpdateProjectOptions, +} from './interfaces' +import { + deserializeProject, + deserializeProjectList, + serializeCreateProjectOptions, + serializeUpdateProjectOptions, +} from './serializers' import { Expandable, List, PaginationOptions } from '@/types' export class Projects { @@ -18,4 +29,42 @@ export class Projects { return deserializeProjectList(data) } + + /** + * Get a project's information by ID. + */ + async get( + id: string, + options: Expandable<'primary_domain'> + ): Promise { + const { data } = await this.agency.get(`projects/${id}`, { + query: options, + }) + + return deserializeProject(data) + } + + /** + * Add a project to your agency. + */ + async create(payload: CreateProjectOptions): Promise { + const { data } = await this.agency.post< + ProjectResponse, + SerializedCreateProjectOptions + >('projects', serializeCreateProjectOptions(payload)) + + return deserializeProject(data) + } + + /** + * Update a project in your agency. + */ + async update(id: string, payload: {}): Promise { + const { data } = await this.agency.patch< + ProjectResponse, + SerializedUpdateProjectOptions + >(`projects/${id}`, serializeUpdateProjectOptions(payload)) + + return deserializeProject(data) + } } diff --git a/src/resources/agency/projects/serializers/create-project-options.serializer.ts b/src/resources/agency/projects/serializers/create-project-options.serializer.ts new file mode 100644 index 0000000..7568e17 --- /dev/null +++ b/src/resources/agency/projects/serializers/create-project-options.serializer.ts @@ -0,0 +1,14 @@ +import { + CreateProjectOptions, + SerializedCreateProjectOptions, +} from '../interfaces' + +export const serializeCreateProjectOptions = ( + options: CreateProjectOptions +): SerializedCreateProjectOptions => ({ + name: options.name, + handle: options.handle, + timezone: options.timezone, + description: options.description, + subdomain: options.subdomain, +}) diff --git a/src/resources/agency/projects/serializers/index.ts b/src/resources/agency/projects/serializers/index.ts index 75cddb7..c839eca 100644 --- a/src/resources/agency/projects/serializers/index.ts +++ b/src/resources/agency/projects/serializers/index.ts @@ -1 +1,3 @@ +export * from './create-project-options.serializer' export * from './project.serializer' +export * from './update-project-options.serializer' diff --git a/src/resources/agency/projects/serializers/update-project-options.serializer.ts b/src/resources/agency/projects/serializers/update-project-options.serializer.ts new file mode 100644 index 0000000..8e9e5f7 --- /dev/null +++ b/src/resources/agency/projects/serializers/update-project-options.serializer.ts @@ -0,0 +1,14 @@ +import { + SerializedUpdateProjectOptions, + UpdateProjectOptions, +} from '../interfaces' + +export const serializeUpdateProjectOptions = ( + options: UpdateProjectOptions +): SerializedUpdateProjectOptions => ({ + name: options.name, + description: options.description, + password: options.password, + timezone: options.timezone, + primary_domain: options.primaryDomain, +}) From eedbea509ab3f79b51e609d62a2d246a60eb2b32 Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Thu, 18 Jul 2024 11:58:16 +1200 Subject: [PATCH 04/12] add additional project methods --- src/resources/agency/domains/domains.ts | 6 +- .../agency/projects/interfaces/index.ts | 1 + .../search-project-options.interface.ts | 7 ++ src/resources/agency/projects/projects.ts | 119 +++++++++++++++++- 4 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 src/resources/agency/projects/interfaces/search-project-options.interface.ts diff --git a/src/resources/agency/domains/domains.ts b/src/resources/agency/domains/domains.ts index 19ddc81..91c6eb9 100644 --- a/src/resources/agency/domains/domains.ts +++ b/src/resources/agency/domains/domains.ts @@ -110,12 +110,10 @@ export class Domains { /** * Search for domains in your agency. */ - async search( - searchDomainOptions: SearchDomainOptions - ): Promise> { + async search(payload: SearchDomainOptions): Promise> { const { data } = await this.agency.post>( 'domains/search', - searchDomainOptions + payload ) return deserializeDomainList(data) } diff --git a/src/resources/agency/projects/interfaces/index.ts b/src/resources/agency/projects/interfaces/index.ts index a0d2208..debe352 100644 --- a/src/resources/agency/projects/interfaces/index.ts +++ b/src/resources/agency/projects/interfaces/index.ts @@ -1,3 +1,4 @@ export * from './create-project-options.interface' export * from './project.interface' +export * from './search-project-options.interface' export * from './update-project-options.interface' diff --git a/src/resources/agency/projects/interfaces/search-project-options.interface.ts b/src/resources/agency/projects/interfaces/search-project-options.interface.ts new file mode 100644 index 0000000..222468c --- /dev/null +++ b/src/resources/agency/projects/interfaces/search-project-options.interface.ts @@ -0,0 +1,7 @@ +export interface SearchProjectOptions { + name: string +} + +export interface SerializedSearchProjectOptions { + name: string +} diff --git a/src/resources/agency/projects/projects.ts b/src/resources/agency/projects/projects.ts index 34d8822..c93cc70 100644 --- a/src/resources/agency/projects/projects.ts +++ b/src/resources/agency/projects/projects.ts @@ -3,16 +3,26 @@ import { CreateProjectOptions, Project, ProjectResponse, + SearchProjectOptions, SerializedCreateProjectOptions, SerializedUpdateProjectOptions, + UpdateProjectOptions, } from './interfaces' +import { Domain, DomainResponse } from '../domains/interfaces' import { deserializeProject, deserializeProjectList, serializeCreateProjectOptions, serializeUpdateProjectOptions, } from './serializers' -import { Expandable, List, PaginationOptions } from '@/types' +import { deserializeDomainList } from '../domains/serializers' +import { + DeletedResponse, + Expandable, + List, + ListResponse, + PaginationOptions, +} from '@/types' export class Projects { constructor(private readonly agency: Agency) {} @@ -35,7 +45,7 @@ export class Projects { */ async get( id: string, - options: Expandable<'primary_domain'> + options?: Expandable<'primary_domain'> ): Promise { const { data } = await this.agency.get(`projects/${id}`, { query: options, @@ -59,7 +69,7 @@ export class Projects { /** * Update a project in your agency. */ - async update(id: string, payload: {}): Promise { + async update(id: string, payload: UpdateProjectOptions): Promise { const { data } = await this.agency.patch< ProjectResponse, SerializedUpdateProjectOptions @@ -67,4 +77,107 @@ export class Projects { return deserializeProject(data) } + + /** + * Archive a project in your agency. + */ + async remove(id: string): Promise { + const { data } = await this.agency.delete(`projects/${id}`) + + return data + } + + /** + * Retrieve a list of archived projects in your agency. + */ + async archived(options?: PaginationOptions): Promise> { + const { data } = await this.agency.get>( + 'projects/archived', + { query: options } + ) + + return deserializeProjectList(data) + } + + /** + * Restore a project with the given ID in your agency. + */ + async restore(id: string): Promise { + const { data } = await this.agency.delete( + `projects/${id}/archived` + ) + + return deserializeProject(data) + } + + /** + * Retrieve the domains for a project in your agency. + */ + async domains( + id: string, + options?: PaginationOptions + ): Promise> { + const { data } = await this.agency.get>( + `projects/${id}/domains`, + { query: options } + ) + + return deserializeDomainList(data) + } + + /** + * Search for projects in your agency. + */ + async search( + payload: SearchProjectOptions, + options?: Expandable<'primary_domain'> + ): Promise> { + const { data } = await this.agency.post>( + 'projects/search', + payload, + { + query: options, + } + ) + + return deserializeProjectList(data) + } + + /** + * Publish a project with the given ID. + * + * NOTE: An active Blutui subscription is required to perform this action. + */ + async publish(id: string): Promise { + const { data } = await this.agency.post( + `projects/${id}/publish`, + {} + ) + + return deserializeProject(data) + } + + /** + * Republish a project with the given ID. + */ + async republish(id: string): Promise { + const { data } = await this.agency.post( + `projects/${id}/republish`, + {} + ) + + return deserializeProject(data) + } + + /** + * Unpublish a project with the given ID. + */ + async unpublish(id: string): Promise { + const { data } = await this.agency.post( + `projects/${id}/unpublish`, + {} + ) + + return deserializeProject(data) + } } From 3719004595ce668dd92f01472d207f34c1fcf89d Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Thu, 18 Jul 2024 14:12:41 +1200 Subject: [PATCH 05/12] add tests for project resource --- src/resources/agency/domains/domains.spec.ts | 2 +- .../projects/fixtures/project-list.json | 15 +- .../agency/projects/fixtures/project.json | 15 +- .../agency/projects/projects.spec.ts | 213 +++++++++++++++++- 4 files changed, 240 insertions(+), 5 deletions(-) diff --git a/src/resources/agency/domains/domains.spec.ts b/src/resources/agency/domains/domains.spec.ts index 40c0cde..77d48bc 100644 --- a/src/resources/agency/domains/domains.spec.ts +++ b/src/resources/agency/domains/domains.spec.ts @@ -117,7 +117,7 @@ describe('Domain', () => { }) describe('search', () => { - it('Search for domains in your agency.', async () => { + it('can search for domains', async () => { fetchOnce(domainListFixture) await blutui.agency('foo').domains.search({ name: 'example.com' }) diff --git a/src/resources/agency/projects/fixtures/project-list.json b/src/resources/agency/projects/fixtures/project-list.json index b200003..4bb67c9 100644 --- a/src/resources/agency/projects/fixtures/project-list.json +++ b/src/resources/agency/projects/fixtures/project-list.json @@ -2,7 +2,20 @@ "object": "list", "data": [ { - "id": "" + "id": "99bc147e-966c-4dd0-8def-de817c63cf41", + "object": "project", + "name": "One", + "description": "One", + "image": null, + "handle": "one", + "password": "SorKVQWV", + "timezone": "UTC", + "subdomain": "one", + "primary_domain": "9bfdb42b-1bf0-4510-978e-46aa329f8efa", + "published": true, + "processed": true, + "created_at": 1720758022, + "updated_at": 1720758046 } ], "meta": { diff --git a/src/resources/agency/projects/fixtures/project.json b/src/resources/agency/projects/fixtures/project.json index 11e3ccd..96e0514 100644 --- a/src/resources/agency/projects/fixtures/project.json +++ b/src/resources/agency/projects/fixtures/project.json @@ -1,3 +1,16 @@ { - "id": "" + "id": "99bc147e-966c-4dd0-8def-de817c63cf41", + "object": "project", + "name": "One", + "description": "One", + "image": null, + "handle": "one", + "password": "SorKVQWV", + "timezone": "UTC", + "subdomain": "one", + "primary_domain": "9bfdb42b-1bf0-4510-978e-46aa329f8efa", + "published": true, + "processed": true, + "created_at": 1720758022, + "updated_at": 1720758046 } diff --git a/src/resources/agency/projects/projects.spec.ts b/src/resources/agency/projects/projects.spec.ts index 91cbdb0..81979d1 100644 --- a/src/resources/agency/projects/projects.spec.ts +++ b/src/resources/agency/projects/projects.spec.ts @@ -2,8 +2,9 @@ import fetch from 'jest-fetch-mock' import { Blutui } from '@/blutui' import { fetchOnce, fetchURL } from '@/utils/testing' -// import projectFixture from './fixtures/project.json' +import projectFixture from './fixtures/project.json' import projectListFixture from './fixtures/project-list.json' +import domainListFixture from '../domains/fixtures/domain-list.json' const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' @@ -30,11 +31,219 @@ describe('Project', () => { .projects.list({ expand: ['primary_domain'] }) expect(fetchURL()).toBe( - blutui.baseURL + `/v1/agencies/foo/projects?expand%5B%5D=primary_domain` + encodeURI( + blutui.baseURL + `/v1/agencies/foo/projects?expand[]=primary_domain` + ) ) expect(projects).toMatchObject({ object: 'list', }) }) }) + + describe('get', () => { + it('can retrieve a project', async () => { + fetchOnce(projectFixture) + const project = await blutui.agency('foo').projects.get(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + `/v1/agencies/foo/projects/${projectFixture.id}` + ) + expect(project).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + object: 'project', + }) + }) + + it('can retrieve a project with an expanded primary domain', async () => { + fetchOnce(projectFixture) + const project = await blutui + .agency('foo') + .projects.get(projectFixture.id, { expand: ['primary_domain'] }) + + expect(fetchURL()).toBe( + encodeURI( + blutui.baseURL + + `/v1/agencies/foo/projects/${projectFixture.id}?expand[]=primary_domain` + ) + ) + expect(project).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + object: 'project', + }) + }) + }) + + describe('create', () => { + it('can create a new project', async () => { + fetchOnce(projectFixture) + const project = await blutui.agency('foo').projects.create({ + name: 'One', + }) + + expect(fetchURL()).toBe(blutui.baseURL + '/v1/agencies/foo/projects') + expect(project).toMatchObject({ + object: 'project', + name: 'One', + }) + }) + }) + + describe('update', () => { + it('can update a project', async () => { + fetchOnce(projectFixture) + const project = await blutui + .agency('foo') + .projects.update(projectFixture.id, { + primaryDomain: projectFixture.primary_domain, + }) + + expect(fetchURL()).toBe( + blutui.baseURL + `/v1/agencies/foo/projects/${projectFixture.id}` + ) + expect(project).toMatchObject({ + object: 'project', + primaryDomain: '9bfdb42b-1bf0-4510-978e-46aa329f8efa', + }) + }) + }) + + describe('remove', () => { + it('can archive a project', async () => { + fetchOnce({ id: projectFixture.id, object: 'project', deleted: true }) + const archivedProject = await blutui + .agency('foo') + .projects.remove(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + `/v1/agencies/foo/projects/${projectFixture.id}` + ) + expect(archivedProject).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + object: 'project', + deleted: true, + }) + }) + }) + + describe('archived', () => { + it('can retrieve a list of archived projects', async () => { + fetchOnce(projectListFixture) + const projects = await blutui + .agency('foo') + .projects.archived({ limit: 2 }) + + expect(fetchURL()).toBe( + encodeURI(blutui.baseURL + `/v1/agencies/foo/projects/archived?limit=2`) + ) + expect(projects).toMatchObject({ + object: 'list', + }) + }) + }) + + describe('restore', () => { + it('can restore an archived project', async () => { + fetchOnce(projectFixture) + const project = await blutui + .agency('foo') + .projects.restore(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + + `/v1/agencies/foo/projects/${projectFixture.id}/archived` + ) + expect(project).toMatchObject({ + object: 'project', + name: 'One', + }) + }) + }) + + describe('domains', () => { + it('can retrieve a list of domains for a project', async () => { + fetchOnce(domainListFixture) + const domains = await blutui + .agency('foo') + .projects.domains(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + + `/v1/agencies/foo/projects/${projectFixture.id}/domains` + ) + expect(domains).toMatchObject({ + object: 'list', + }) + }) + }) + + describe('search', () => { + it('can search for projects', async () => { + fetchOnce(projectListFixture) + const projects = await blutui + .agency('foo') + .projects.search({ name: 'One' }) + + expect(fetchURL()).toBe( + blutui.baseURL + `/v1/agencies/foo/projects/search` + ) + expect(projects).toMatchObject({ + object: 'list', + }) + }) + }) + + describe('publish', () => { + it('can publish a project', async () => { + fetchOnce(projectFixture) + const project = await blutui + .agency('foo') + .projects.publish(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + + `/v1/agencies/foo/projects/${projectFixture.id}/publish` + ) + expect(project).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + object: 'project', + }) + }) + }) + + describe('republish', () => { + it('can republish a project', async () => { + fetchOnce(projectFixture) + const project = await blutui + .agency('foo') + .projects.republish(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + + `/v1/agencies/foo/projects/${projectFixture.id}/republish` + ) + expect(project).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + object: 'project', + }) + }) + }) + + describe('unpublish', () => { + it('can unpublish a project', async () => { + fetchOnce(projectFixture) + const project = await blutui + .agency('foo') + .projects.unpublish(projectFixture.id) + + expect(fetchURL()).toBe( + blutui.baseURL + + `/v1/agencies/foo/projects/${projectFixture.id}/unpublish` + ) + expect(project).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + object: 'project', + }) + }) + }) }) From 8be6bdbf0eb08e53bfd6c73ec14d5d1c0a2dd6f4 Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Mon, 22 Jul 2024 11:25:34 +1200 Subject: [PATCH 06/12] fix lint issues --- src/resources/agency/domains/domains.ts | 1 + .../agency/projects/projects.spec.ts | 34 ++++------ src/resources/agency/projects/projects.ts | 68 ++++++++++--------- .../create-project-options.serializer.ts | 2 +- .../serializers/project.serializer.ts | 5 +- .../update-project-options.serializer.ts | 2 +- src/utils/client.ts | 5 +- 7 files changed, 59 insertions(+), 58 deletions(-) diff --git a/src/resources/agency/domains/domains.ts b/src/resources/agency/domains/domains.ts index 2a28a1b..6363625 100644 --- a/src/resources/agency/domains/domains.ts +++ b/src/resources/agency/domains/domains.ts @@ -19,6 +19,7 @@ import type { } from './interfaces' import type { DeletedResponse, + Expandable, List, ListResponse, PaginationOptions, diff --git a/src/resources/agency/projects/projects.spec.ts b/src/resources/agency/projects/projects.spec.ts index 81979d1..5e95b57 100644 --- a/src/resources/agency/projects/projects.spec.ts +++ b/src/resources/agency/projects/projects.spec.ts @@ -18,7 +18,7 @@ describe('Project', () => { fetchOnce(projectListFixture) const projects = await blutui.agency('foo').projects.list() - expect(fetchURL()).toBe(blutui.baseURL + `/v1/agencies/foo/projects`) + expect(fetchURL()).toBe(`${blutui.baseURL}/v1/agencies/foo/projects`) expect(projects).toMatchObject({ object: 'list', }) @@ -32,7 +32,7 @@ describe('Project', () => { expect(fetchURL()).toBe( encodeURI( - blutui.baseURL + `/v1/agencies/foo/projects?expand[]=primary_domain` + `${blutui.baseURL}/v1/agencies/foo/projects?expand[]=primary_domain` ) ) expect(projects).toMatchObject({ @@ -47,7 +47,7 @@ describe('Project', () => { const project = await blutui.agency('foo').projects.get(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + `/v1/agencies/foo/projects/${projectFixture.id}` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}` ) expect(project).toMatchObject({ id: '99bc147e-966c-4dd0-8def-de817c63cf41', @@ -63,8 +63,7 @@ describe('Project', () => { expect(fetchURL()).toBe( encodeURI( - blutui.baseURL + - `/v1/agencies/foo/projects/${projectFixture.id}?expand[]=primary_domain` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}?expand[]=primary_domain` ) ) expect(project).toMatchObject({ @@ -81,7 +80,7 @@ describe('Project', () => { name: 'One', }) - expect(fetchURL()).toBe(blutui.baseURL + '/v1/agencies/foo/projects') + expect(fetchURL()).toBe(`${blutui.baseURL}/v1/agencies/foo/projects`) expect(project).toMatchObject({ object: 'project', name: 'One', @@ -99,7 +98,7 @@ describe('Project', () => { }) expect(fetchURL()).toBe( - blutui.baseURL + `/v1/agencies/foo/projects/${projectFixture.id}` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}` ) expect(project).toMatchObject({ object: 'project', @@ -116,7 +115,7 @@ describe('Project', () => { .projects.remove(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + `/v1/agencies/foo/projects/${projectFixture.id}` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}` ) expect(archivedProject).toMatchObject({ id: '99bc147e-966c-4dd0-8def-de817c63cf41', @@ -134,7 +133,7 @@ describe('Project', () => { .projects.archived({ limit: 2 }) expect(fetchURL()).toBe( - encodeURI(blutui.baseURL + `/v1/agencies/foo/projects/archived?limit=2`) + encodeURI(`${blutui.baseURL}/v1/agencies/foo/projects/archived?limit=2`) ) expect(projects).toMatchObject({ object: 'list', @@ -150,8 +149,7 @@ describe('Project', () => { .projects.restore(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + - `/v1/agencies/foo/projects/${projectFixture.id}/archived` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}/archived` ) expect(project).toMatchObject({ object: 'project', @@ -168,8 +166,7 @@ describe('Project', () => { .projects.domains(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + - `/v1/agencies/foo/projects/${projectFixture.id}/domains` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}/domains` ) expect(domains).toMatchObject({ object: 'list', @@ -185,7 +182,7 @@ describe('Project', () => { .projects.search({ name: 'One' }) expect(fetchURL()).toBe( - blutui.baseURL + `/v1/agencies/foo/projects/search` + `${blutui.baseURL}/v1/agencies/foo/projects/search` ) expect(projects).toMatchObject({ object: 'list', @@ -201,8 +198,7 @@ describe('Project', () => { .projects.publish(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + - `/v1/agencies/foo/projects/${projectFixture.id}/publish` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}/publish` ) expect(project).toMatchObject({ id: '99bc147e-966c-4dd0-8def-de817c63cf41', @@ -219,8 +215,7 @@ describe('Project', () => { .projects.republish(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + - `/v1/agencies/foo/projects/${projectFixture.id}/republish` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}/republish` ) expect(project).toMatchObject({ id: '99bc147e-966c-4dd0-8def-de817c63cf41', @@ -237,8 +232,7 @@ describe('Project', () => { .projects.unpublish(projectFixture.id) expect(fetchURL()).toBe( - blutui.baseURL + - `/v1/agencies/foo/projects/${projectFixture.id}/unpublish` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}/unpublish` ) expect(project).toMatchObject({ id: '99bc147e-966c-4dd0-8def-de817c63cf41', diff --git a/src/resources/agency/projects/projects.ts b/src/resources/agency/projects/projects.ts index c93cc70..e36a7a9 100644 --- a/src/resources/agency/projects/projects.ts +++ b/src/resources/agency/projects/projects.ts @@ -1,22 +1,24 @@ -import { Agency } from '@/agency' import { + deserializeProject, + deserializeProjectList, + serializeCreateProjectOptions, + serializeUpdateProjectOptions, +} from './serializers' +import { deserializeDomainList } from '../domains/serializers' + +import type { Agency } from '@/agency' +import type { CreateProjectOptions, Project, ProjectResponse, SearchProjectOptions, SerializedCreateProjectOptions, + SerializedSearchProjectOptions, SerializedUpdateProjectOptions, UpdateProjectOptions, } from './interfaces' -import { Domain, DomainResponse } from '../domains/interfaces' -import { - deserializeProject, - deserializeProjectList, - serializeCreateProjectOptions, - serializeUpdateProjectOptions, -} from './serializers' -import { deserializeDomainList } from '../domains/serializers' -import { +import type { Domain, DomainResponse } from '../domains/interfaces' +import type { DeletedResponse, Expandable, List, @@ -33,9 +35,12 @@ export class Projects { async list( options?: PaginationOptions & Expandable<'primary_domain'> ): Promise> { - const { data } = await this.agency.get('projects', { - query: options, - }) + const { data } = await this.agency.get>( + 'projects', + { + query: options, + } + ) return deserializeProjectList(data) } @@ -132,13 +137,12 @@ export class Projects { payload: SearchProjectOptions, options?: Expandable<'primary_domain'> ): Promise> { - const { data } = await this.agency.post>( - 'projects/search', - payload, - { - query: options, - } - ) + const { data } = await this.agency.post< + ListResponse, + SerializedSearchProjectOptions + >('projects/search', payload, { + query: options, + }) return deserializeProjectList(data) } @@ -149,10 +153,10 @@ export class Projects { * NOTE: An active Blutui subscription is required to perform this action. */ async publish(id: string): Promise { - const { data } = await this.agency.post( - `projects/${id}/publish`, - {} - ) + const { data } = await this.agency.post< + ProjectResponse, + Record + >(`projects/${id}/publish`, {}) return deserializeProject(data) } @@ -161,10 +165,10 @@ export class Projects { * Republish a project with the given ID. */ async republish(id: string): Promise { - const { data } = await this.agency.post( - `projects/${id}/republish`, - {} - ) + const { data } = await this.agency.post< + ProjectResponse, + Record + >(`projects/${id}/republish`, {}) return deserializeProject(data) } @@ -173,10 +177,10 @@ export class Projects { * Unpublish a project with the given ID. */ async unpublish(id: string): Promise { - const { data } = await this.agency.post( - `projects/${id}/unpublish`, - {} - ) + const { data } = await this.agency.post< + ProjectResponse, + Record + >(`projects/${id}/unpublish`, {}) return deserializeProject(data) } diff --git a/src/resources/agency/projects/serializers/create-project-options.serializer.ts b/src/resources/agency/projects/serializers/create-project-options.serializer.ts index 7568e17..e63565c 100644 --- a/src/resources/agency/projects/serializers/create-project-options.serializer.ts +++ b/src/resources/agency/projects/serializers/create-project-options.serializer.ts @@ -1,4 +1,4 @@ -import { +import type { CreateProjectOptions, SerializedCreateProjectOptions, } from '../interfaces' diff --git a/src/resources/agency/projects/serializers/project.serializer.ts b/src/resources/agency/projects/serializers/project.serializer.ts index 689aa04..5196913 100644 --- a/src/resources/agency/projects/serializers/project.serializer.ts +++ b/src/resources/agency/projects/serializers/project.serializer.ts @@ -1,7 +1,8 @@ -import { List, ListResponse } from '@/types' -import { Project, ProjectResponse } from '../interfaces' import { deserializePaginationMeta } from '@/utils/serializers' +import type { Project, ProjectResponse } from '../interfaces' +import type { List, ListResponse } from '@/types' + export const deserializeProject = (project: ProjectResponse): Project => ({ id: project.id, object: project.object, diff --git a/src/resources/agency/projects/serializers/update-project-options.serializer.ts b/src/resources/agency/projects/serializers/update-project-options.serializer.ts index 8e9e5f7..ecaa641 100644 --- a/src/resources/agency/projects/serializers/update-project-options.serializer.ts +++ b/src/resources/agency/projects/serializers/update-project-options.serializer.ts @@ -1,4 +1,4 @@ -import { +import type { SerializedUpdateProjectOptions, UpdateProjectOptions, } from '../interfaces' diff --git a/src/utils/client.ts b/src/utils/client.ts index 8bdf41e..0f936f5 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -124,8 +124,9 @@ function getQueryString(queryObj?: Record) { if (element !== '' && element !== undefined) sanitizedQueryObj.push([`${param}[]`, element]) } - } else if (value && typeof value === 'string') { - sanitizedQueryObj.push([param, value]) + } else if (value) { + const newValue = String(value) + sanitizedQueryObj.push([param, newValue]) } } From bc640ff3862ea05c4bb4cccc514d0b18126aec86 Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Mon, 22 Jul 2024 12:54:58 +1200 Subject: [PATCH 07/12] add ability to get primary_domain type --- .../fixtures/project-with-primary-domain.json | 25 +++++++++++++++++++ .../projects/interfaces/project.interface.ts | 6 +++-- .../agency/projects/projects.spec.ts | 13 +++++++--- .../serializers/project.serializer.ts | 6 ++++- 4 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 src/resources/agency/projects/fixtures/project-with-primary-domain.json diff --git a/src/resources/agency/projects/fixtures/project-with-primary-domain.json b/src/resources/agency/projects/fixtures/project-with-primary-domain.json new file mode 100644 index 0000000..beef8ba --- /dev/null +++ b/src/resources/agency/projects/fixtures/project-with-primary-domain.json @@ -0,0 +1,25 @@ +{ + "id": "99bc147e-966c-4dd0-8def-de817c63cf41", + "object": "project", + "name": "One", + "description": "One", + "image": null, + "handle": "one", + "password": "SorKVQWV", + "timezone": "UTC", + "subdomain": "one", + "primary_domain": { + "id": "9bfdb42b-1bf0-4510-978e-46aa329f8efa", + "object": "domain", + "name": "example.com", + "token": "08Q8wwsDMIuCwAsudZtXgf3ABkAwqbExgUPWUEPEuMBoWUIH0ie81R27h2elqjk1", + "project": "9bf15409-db06-4c2c-b1ee-3a64c0074092", + "verified_at": null, + "created_at": 1716170007, + "updated_at": 1716170007 + }, + "published": true, + "processed": true, + "created_at": 1720758022, + "updated_at": 1720758046 +} diff --git a/src/resources/agency/projects/interfaces/project.interface.ts b/src/resources/agency/projects/interfaces/project.interface.ts index 2f36834..5bc2ac9 100644 --- a/src/resources/agency/projects/interfaces/project.interface.ts +++ b/src/resources/agency/projects/interfaces/project.interface.ts @@ -1,3 +1,5 @@ +import type { Domain, DomainResponse } from '../../domains/interfaces' + export interface Project { id: string object: 'project' @@ -8,7 +10,7 @@ export interface Project { password: string timezone: string subdomain: string - primaryDomain: string | Record + primaryDomain: string | null | Domain published: boolean processed: boolean createdAt: number @@ -26,7 +28,7 @@ export interface ProjectResponse { password: string timezone: string subdomain: string - primary_domain: string | Record + primary_domain: string | null | DomainResponse published: boolean processed: boolean created_at: number diff --git a/src/resources/agency/projects/projects.spec.ts b/src/resources/agency/projects/projects.spec.ts index 5e95b57..9b43a8a 100644 --- a/src/resources/agency/projects/projects.spec.ts +++ b/src/resources/agency/projects/projects.spec.ts @@ -3,6 +3,7 @@ import { Blutui } from '@/blutui' import { fetchOnce, fetchURL } from '@/utils/testing' import projectFixture from './fixtures/project.json' +import projectWithPrimaryDomainFixture from './fixtures/project-with-primary-domain.json' import projectListFixture from './fixtures/project-list.json' import domainListFixture from '../domains/fixtures/domain-list.json' @@ -56,20 +57,26 @@ describe('Project', () => { }) it('can retrieve a project with an expanded primary domain', async () => { - fetchOnce(projectFixture) + fetchOnce(projectWithPrimaryDomainFixture) const project = await blutui .agency('foo') - .projects.get(projectFixture.id, { expand: ['primary_domain'] }) + .projects.get(projectWithPrimaryDomainFixture.id, { + expand: ['primary_domain'], + }) expect(fetchURL()).toBe( encodeURI( - `${blutui.baseURL}/v1/agencies/foo/projects/${projectFixture.id}?expand[]=primary_domain` + `${blutui.baseURL}/v1/agencies/foo/projects/${projectWithPrimaryDomainFixture.id}?expand[]=primary_domain` ) ) expect(project).toMatchObject({ id: '99bc147e-966c-4dd0-8def-de817c63cf41', object: 'project', }) + expect(project.primaryDomain).toMatchObject({ + id: '9bfdb42b-1bf0-4510-978e-46aa329f8efa', + verifiedAt: null, + }) }) }) diff --git a/src/resources/agency/projects/serializers/project.serializer.ts b/src/resources/agency/projects/serializers/project.serializer.ts index 5196913..94c4670 100644 --- a/src/resources/agency/projects/serializers/project.serializer.ts +++ b/src/resources/agency/projects/serializers/project.serializer.ts @@ -1,4 +1,5 @@ import { deserializePaginationMeta } from '@/utils/serializers' +import { deserializeDomain } from '../../domains/serializers' import type { Project, ProjectResponse } from '../interfaces' import type { List, ListResponse } from '@/types' @@ -13,7 +14,10 @@ export const deserializeProject = (project: ProjectResponse): Project => ({ password: project.password, timezone: project.timezone, subdomain: project.subdomain, - primaryDomain: project.primary_domain, + primaryDomain: + project.primary_domain instanceof Object + ? deserializeDomain(project.primary_domain) + : project.primary_domain, published: project.published, processed: project.processed, createdAt: project.created_at, From c484c97136bc3c22666146d52e9845c325df4df2 Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Mon, 22 Jul 2024 13:00:19 +1200 Subject: [PATCH 08/12] add is string check --- src/resources/agency/projects/projects.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/agency/projects/projects.spec.ts b/src/resources/agency/projects/projects.spec.ts index 9b43a8a..b347b4f 100644 --- a/src/resources/agency/projects/projects.spec.ts +++ b/src/resources/agency/projects/projects.spec.ts @@ -54,6 +54,7 @@ describe('Project', () => { id: '99bc147e-966c-4dd0-8def-de817c63cf41', object: 'project', }) + expect(typeof project.primaryDomain).toBe('string') }) it('can retrieve a project with an expanded primary domain', async () => { From d26515fb58edee249629d01130e29876981d175d Mon Sep 17 00:00:00 2001 From: Jayan Ratna Date: Mon, 22 Jul 2024 13:09:35 +1200 Subject: [PATCH 09/12] add the ability to get project type from Domain --- src/resources/agency/domains/domains.spec.ts | 19 ++++++++++---- .../domains/fixtures/domain-with-project.json | 25 +++++++++++++++++++ .../domains/interfaces/domain.interface.ts | 10 +++++--- .../domains/serializers/domain.serializer.ts | 6 ++++- 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 src/resources/agency/domains/fixtures/domain-with-project.json diff --git a/src/resources/agency/domains/domains.spec.ts b/src/resources/agency/domains/domains.spec.ts index 3e28d50..990de5f 100644 --- a/src/resources/agency/domains/domains.spec.ts +++ b/src/resources/agency/domains/domains.spec.ts @@ -1,7 +1,9 @@ import fetch from 'jest-fetch-mock' import { Blutui } from '@/blutui' import { fetchOnce, fetchURL } from '@/utils/testing' + import domainFixture from './fixtures/domain.json' +import domainWithProjectFixture from './fixtures/domain-with-project.json' import domainListFixture from './fixtures/domain-list.json' const accessToken = @@ -34,21 +36,28 @@ describe('Domain', () => { expect(domain).toMatchObject({ object: 'domain', }) + expect(typeof domain.project).toBe('string') }) it('can retrieve a domain information with project', async () => { - fetchOnce(domainFixture) - const domain = await blutui.agency('foo').domains.get(domainFixture.id, { - expand: ['project'], - }) + fetchOnce(domainWithProjectFixture) + const domain = await blutui + .agency('foo') + .domains.get(domainWithProjectFixture.id, { + expand: ['project'], + }) expect(fetchURL()).toContain( encodeURI( - `/v1/agencies/foo/domains/${domainFixture.id}?expand[]=project` + `/v1/agencies/foo/domains/${domainWithProjectFixture.id}?expand[]=project` ) ) expect(domain).toMatchObject({ object: 'domain', }) + expect(domain.project).toMatchObject({ + id: '99bc147e-966c-4dd0-8def-de817c63cf41', + createdAt: 1720758022, + }) }) }) diff --git a/src/resources/agency/domains/fixtures/domain-with-project.json b/src/resources/agency/domains/fixtures/domain-with-project.json new file mode 100644 index 0000000..accb1db --- /dev/null +++ b/src/resources/agency/domains/fixtures/domain-with-project.json @@ -0,0 +1,25 @@ +{ + "id": "9bfdb42b-1bf0-4510-978e-46aa329f8efa", + "object": "domain", + "name": "example.com", + "token": "08Q8wwsDMIuCwAsudZtXgf3ABkAwqbExgUPWUEPEuMBoWUIH0ie81R27h2elqjk1", + "project": { + "id": "99bc147e-966c-4dd0-8def-de817c63cf41", + "object": "project", + "name": "One", + "description": "One", + "image": null, + "handle": "one", + "password": "SorKVQWV", + "timezone": "UTC", + "subdomain": "one", + "primary_domain": "9bfdb42b-1bf0-4510-978e-46aa329f8efa", + "published": true, + "processed": true, + "created_at": 1720758022, + "updated_at": 1720758046 + }, + "verified_at": null, + "created_at": 1716170007, + "updated_at": 1716170007 +} diff --git a/src/resources/agency/domains/interfaces/domain.interface.ts b/src/resources/agency/domains/interfaces/domain.interface.ts index c41189e..a43e54c 100644 --- a/src/resources/agency/domains/interfaces/domain.interface.ts +++ b/src/resources/agency/domains/interfaces/domain.interface.ts @@ -1,10 +1,12 @@ +import type { Project, ProjectResponse } from '../../projects/interfaces' + export interface Domain { id: string object: 'domain' name: string token: string - project: string | Record - verifiedAt: number + project: string | null | Project + verifiedAt: number | null createdAt: number updatedAt: number } @@ -14,8 +16,8 @@ export interface DomainResponse { object: 'domain' name: string token: string - project: string | Record - verified_at: number + project: string | null | ProjectResponse + verified_at: number | null created_at: number updated_at: number } diff --git a/src/resources/agency/domains/serializers/domain.serializer.ts b/src/resources/agency/domains/serializers/domain.serializer.ts index 44dc35a..d6e1d6e 100644 --- a/src/resources/agency/domains/serializers/domain.serializer.ts +++ b/src/resources/agency/domains/serializers/domain.serializer.ts @@ -2,13 +2,17 @@ import { deserializePaginationMeta } from '@/utils/serializers' import type { List, ListResponse } from '@/types' import type { Domain, DomainResponse } from '../interfaces' +import { deserializeProject } from '../../projects/serializers' export const deserializeDomain = (domain: DomainResponse): Domain => ({ id: domain.id, object: domain.object, name: domain.name, token: domain.token, - project: domain.project, + project: + domain.project instanceof Object + ? deserializeProject(domain.project) + : domain.project, verifiedAt: domain.verified_at, createdAt: domain.created_at, updatedAt: domain.updated_at, From e3f17d2726d1e0fcb2980072141243cf1a0a350c Mon Sep 17 00:00:00 2001 From: Jayan Ratna <30396013+jayan-blutui@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:32:28 +1200 Subject: [PATCH 10/12] Update src/utils/client.ts Co-authored-by: Michael Liu <162056370+chengfang-blutui@users.noreply.github.com> --- src/utils/client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/client.ts b/src/utils/client.ts index 0f936f5..c251599 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -125,7 +125,6 @@ function getQueryString(queryObj?: Record) { sanitizedQueryObj.push([`${param}[]`, element]) } } else if (value) { - const newValue = String(value) sanitizedQueryObj.push([param, newValue]) } } From 2e13e5afde5738930c28c0c643547d567d764643 Mon Sep 17 00:00:00 2001 From: Jayan Ratna <30396013+jayan-blutui@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:33:08 +1200 Subject: [PATCH 11/12] Update src/utils/client.ts Co-authored-by: Michael Liu <162056370+chengfang-blutui@users.noreply.github.com> --- src/utils/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/client.ts b/src/utils/client.ts index c251599..a020176 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -125,7 +125,7 @@ function getQueryString(queryObj?: Record) { sanitizedQueryObj.push([`${param}[]`, element]) } } else if (value) { - sanitizedQueryObj.push([param, newValue]) + sanitizedQueryObj.push([param, String(value)]) } } From 444f4379998e60cbff17e4edd9168c10eb9b10b9 Mon Sep 17 00:00:00 2001 From: Jayan Ratna <30396013+jayan-blutui@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:34:34 +1200 Subject: [PATCH 12/12] Update src/utils/client.ts Co-authored-by: Michael Liu <162056370+chengfang-blutui@users.noreply.github.com> --- src/utils/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/client.ts b/src/utils/client.ts index a020176..f4b4da9 100644 --- a/src/utils/client.ts +++ b/src/utils/client.ts @@ -122,7 +122,7 @@ function getQueryString(queryObj?: Record) { if (Array.isArray(value)) { for (const element of value) { if (element !== '' && element !== undefined) - sanitizedQueryObj.push([`${param}[]`, element]) + sanitizedQueryObj.push([`${param}[]`, String(element)]) } } else if (value) { sanitizedQueryObj.push([param, String(value)])