diff --git a/src/index.ts b/src/index.ts index 405c91e3d..0c4a3bee9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ import { SourceFiles } from './sourceFiles'; import { SourceStrings } from './sourceStrings'; import { StringComments } from './stringComments'; import { StringCorrections } from './stringCorrections'; +import { StyleGuides } from './styleGuides'; import { StringTranslations } from './stringTranslations'; import { Tasks } from './tasks'; import { Teams } from './teams'; @@ -56,6 +57,7 @@ export * from './sourceFiles'; export * from './sourceStrings'; export * from './stringComments'; export * from './stringCorrections'; +export * from './styleGuides'; export * from './stringTranslations'; export * from './tasks'; export * from './teams'; @@ -108,6 +110,7 @@ export default class Client extends CrowdinApi { readonly securityLogsApi: SecurityLogs; readonly fieldsApi: Fields; readonly stringCorrectionsApi: StringCorrections; + readonly styleGuidesApi: StyleGuides; constructor(credentials: Credentials, config?: ClientConfig) { super(credentials, config); @@ -144,6 +147,7 @@ export default class Client extends CrowdinApi { this.securityLogsApi = new SecurityLogs(credentials, config); this.fieldsApi = new Fields(credentials, config); this.stringCorrectionsApi = new StringCorrections(credentials, config); + this.styleGuidesApi = new StyleGuides(credentials, config); } } diff --git a/src/styleGuides/index.ts b/src/styleGuides/index.ts new file mode 100644 index 000000000..43ba7a9b4 --- /dev/null +++ b/src/styleGuides/index.ts @@ -0,0 +1,88 @@ +import { CrowdinApi, PaginationOptions, PatchRequest, ResponseList, ResponseObject } from '../core'; + +export class StyleGuides extends CrowdinApi { + /** + * @param options optional parameters for the request + * @see https://developer.crowdin.com/api/v2/#operation/api.style-guides.getMany + */ + listStyleGuides( + options?: StyleGuidesModel.ListStyleGuidesOptions, + ): Promise> { + let url = `${this.url}/style-guides`; + url = this.addQueryParam(url, 'orderBy', options?.orderBy); + url = this.addQueryParam(url, 'userId', options?.userId); + return this.getList(url, options?.limit, options?.offset); + } + + /** + * @param request request body + * @see https://developer.crowdin.com/api/v2/#operation/api.style-guides.post + */ + createStyleGuide( + request: StyleGuidesModel.CreateStyleGuideRequest, + ): Promise> { + const url = `${this.url}/style-guides`; + return this.post(url, request, this.defaultConfig()); + } + + /** + * @param styleGuideId style guide identifier + * @see https://developer.crowdin.com/api/v2/#operation/api.style-guides.get + */ + getStyleGuide(styleGuideId: number): Promise> { + const url = `${this.url}/style-guides/${styleGuideId}`; + return this.get(url, this.defaultConfig()); + } + + /** + * @param styleGuideId style guide identifier + * @see https://developer.crowdin.com/api/v2/#operation/api.style-guides.delete + */ + deleteStyleGuide(styleGuideId: number): Promise { + const url = `${this.url}/style-guides/${styleGuideId}`; + return this.delete(url, this.defaultConfig()); + } + + /** + * @param styleGuideId style guide identifier + * @param request request body + * @see https://developer.crowdin.com/api/v2/#operation/api.style-guides.patch + */ + editStyleGuide( + styleGuideId: number, + request: PatchRequest[], + ): Promise> { + const url = `${this.url}/style-guides/${styleGuideId}`; + return this.patch(url, request, this.defaultConfig()); + } +} + +export namespace StyleGuidesModel { + export interface StyleGuide { + id: number; + name: string; + aiInstructions: string | null; + userId: number; + languageIds: string[] | null; + projectIds: number[] | null; + isShared: boolean; + webUrl: string; + downloadLink: string; + createdAt: string; + updatedAt: string; + } + + export interface CreateStyleGuideRequest { + name: string; + storageId: number | null; + aiInstructions?: string; + languageIds?: string[]; + projectIds?: number[]; + isShared?: boolean; + } + + export interface ListStyleGuidesOptions extends PaginationOptions { + orderBy?: string; + userId?: number; + } +} diff --git a/tests/styleGuides/api.test.ts b/tests/styleGuides/api.test.ts new file mode 100644 index 000000000..1ae6bf014 --- /dev/null +++ b/tests/styleGuides/api.test.ts @@ -0,0 +1,127 @@ +import * as nock from 'nock'; +import { Credentials, StyleGuides } from '../../src'; + +describe('Style Guides API', () => { + let scope: nock.Scope; + const credentials: Credentials = { + token: 'testToken', + organization: 'testOrg', + }; + const api: StyleGuides = new StyleGuides(credentials); + const styleGuideId = 2; + const storageId = 1; + const name = "Be My Eyes iOS's Style Guide"; + + const limit = 25; + + beforeAll(() => { + scope = nock(api.url) + .get('/style-guides', undefined, { + reqheaders: { + Authorization: `Bearer ${api.token}`, + }, + }) + .reply(200, { + data: [ + { + data: { + id: styleGuideId, + }, + }, + ], + pagination: { + offset: 0, + limit: limit, + }, + }) + .post( + '/style-guides', + { + name, + storageId, + }, + { + reqheaders: { + Authorization: `Bearer ${api.token}`, + }, + }, + ) + .reply(200, { + data: { + id: styleGuideId, + }, + }) + .get(`/style-guides/${styleGuideId}`, undefined, { + reqheaders: { + Authorization: `Bearer ${api.token}`, + }, + }) + .reply(200, { + data: { + id: styleGuideId, + }, + }) + .delete(`/style-guides/${styleGuideId}`, undefined, { + reqheaders: { + Authorization: `Bearer ${api.token}`, + }, + }) + .reply(200) + .patch( + `/style-guides/${styleGuideId}`, + [ + { + op: 'replace', + path: '/name', + value: name, + }, + ], + { + reqheaders: { + Authorization: `Bearer ${api.token}`, + }, + }, + ) + .reply(200, { + data: { + id: styleGuideId, + }, + }); + }); + + afterAll(() => { + scope.done(); + }); + + it('List style guides', async () => { + const styleGuides = await api.listStyleGuides(); + expect(styleGuides.data.length).toBe(1); + expect(styleGuides.data[0].data.id).toBe(styleGuideId); + expect(styleGuides.pagination.limit).toBe(limit); + }); + + it('Create style guide', async () => { + const styleGuide = await api.createStyleGuide({ name, storageId }); + expect(styleGuide.data.id).toBe(styleGuideId); + }); + + it('Get style guide', async () => { + const styleGuide = await api.getStyleGuide(styleGuideId); + expect(styleGuide.data.id).toBe(styleGuideId); + }); + + it('Delete style guide', async () => { + await api.deleteStyleGuide(styleGuideId); + }); + + it('Edit style guide', async () => { + const styleGuide = await api.editStyleGuide(styleGuideId, [ + { + op: 'replace', + path: '/name', + value: name, + }, + ]); + expect(styleGuide.data.id).toBe(styleGuideId); + }); +});