Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add fields API support #381

Merged
merged 1 commit into from
Mar 25, 2024
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
138 changes: 138 additions & 0 deletions src/fields/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { CrowdinApi, PaginationOptions, PatchRequest, ResponseList, ResponseObject } from '../core';

export class Fields extends CrowdinApi {
/**
* @param options optional parameters for the request
* @see https://developer.crowdin.com/enterprise/api/v2/#operation/api.fields.getMany
*/
listFields(options?: FieldsModel.ListFieldsParams): Promise<ResponseList<FieldsModel.Field>> {
let url = `${this.url}/fields`;
url = this.addQueryParam(url, 'search', options?.search);
url = this.addQueryParam(url, 'entity', options?.entity);
url = this.addQueryParam(url, 'type', options?.type);
return this.getList(url, options?.limit, options?.offset);
}

/**
* @param request request body
* @see https://developer.crowdin.com/enterprise/api/v2/#operation/api.fields.post
*/
addField(request: FieldsModel.AddFieldRequest): Promise<ResponseObject<FieldsModel.Field>> {
const url = `${this.url}/fields`;
return this.post(url, request, this.defaultConfig());
}

/**
* @param fieldId field identifier
* @see https://developer.crowdin.com/enterprise/api/v2/#operation/api.fields.get
*/
getField(fieldId: number): Promise<ResponseObject<FieldsModel.Field>> {
const url = `${this.url}/fields/${fieldId}`;
return this.get(url, this.defaultConfig());
}

/**
* @param fieldId field identifier
* @see https://developer.crowdin.com/api/v2/#operation/api.fields.delete
*/
deleteField(fieldId: number): Promise<void> {
const url = `${this.url}/fields/${fieldId}`;
return this.delete(url, this.defaultConfig());
}

/**
* @param fieldId field identifier
* @param request request body
* @see https://developer.crowdin.com/api/v2/#operation/api.fields.patch
*/
editField(fieldId: number, request: PatchRequest[]): Promise<ResponseObject<FieldsModel.Field>> {
const url = `${this.url}/fields/${fieldId}`;
return this.patch(url, request, this.defaultConfig());
}
}

export namespace FieldsModel {
export type Entity = 'project' | 'user' | 'task' | 'file' | 'translation' | 'string';

export type Type =
| 'checkbox'
| 'radiobuttons'
| 'date'
| 'datetime'
| 'number'
| 'labels'
| 'select'
| 'multiselect'
| 'text'
| 'textarea'
| 'url';

export type Place =
| 'projectCreateModal'
| 'projectHeader'
| 'projectDetails'
| 'projectCrowdsourceDetails'
| 'projectSettings'
| 'projectTaskEditCreate'
| 'projectTaskDetails'
| 'fileDetails'
| 'fileSettings'
| 'userEditModal'
| 'userDetails'
| 'userPopover'
| 'stringEditModal'
| 'stringDetails'
| 'translationUnderContent';

export interface Location {
place: Place;
}

export interface Option {
label: string;
value: string;
}

export interface OtherFieldConfig {
locations: Location[];
}

export interface ListFieldConfig extends OtherFieldConfig {
options: Option[];
}

export interface NumberFieldConfig extends OtherFieldConfig {
min: number;
max: number;
units: string;
}

export type Config = ListFieldConfig | NumberFieldConfig | OtherFieldConfig;

export interface ListFieldsParams extends PaginationOptions {
search?: string;
entity?: Entity;
type?: Type;
}

export interface Field {
id: number;
name: string;
slug: string;
type: Type;
description: string;
entities: Entity[];
config: Config;
createdAt: string;
updatedAt: string;
}

export interface AddFieldRequest {
name: string;
slug: string;
type: Type;
description?: string;
entities: Entity[];
config: Config;
}
}
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Clients } from './clients';
import { ClientConfig, Credentials, CrowdinApi } from './core';
import { Dictionaries } from './dictionaries';
import { Distributions } from './distributions';
import { Fields } from './fields';
import { Glossaries } from './glossaries';
import { Issues } from './issues';
import { Labels } from './labels';
Expand Down Expand Up @@ -36,6 +37,7 @@ export * from './clients';
export * from './core';
export * from './dictionaries';
export * from './distributions';
export * from './fields';
export * from './glossaries';
export * from './issues';
export * from './labels';
Expand Down Expand Up @@ -99,6 +101,7 @@ export default class Client extends CrowdinApi {
readonly notificationsApi: Notifications;
readonly clientsApi: Clients;
readonly securityLogsApi: SecurityLogs;
readonly fieldsApi: Fields;

constructor(credentials: Credentials, config?: ClientConfig) {
super(credentials, config);
Expand Down Expand Up @@ -132,5 +135,6 @@ export default class Client extends CrowdinApi {
this.notificationsApi = new Notifications(credentials, config);
this.clientsApi = new Clients(credentials, config);
this.securityLogsApi = new SecurityLogs(credentials, config);
this.fieldsApi = new Fields(credentials, config);
}
}
159 changes: 159 additions & 0 deletions tests/fields/api.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import * as nock from 'nock';
import { Credentials, Fields } from '../../src/';

describe('Fields API', () => {
let scope: nock.Scope;
const credentials: Credentials = {
token: 'testToken',
organization: 'testOrg',
};
const api: Fields = new Fields(credentials);
const fieldId = 2;
const fieldMock = {
id: fieldId,
name: 'Field 2',
slug: 'field-2',
type: 'text',
entities: ['project', 'user'],
config: {
locations: [
{
place: 'projectHeader',
},
],
},
};

const limit = 25;

beforeAll(() => {
scope = nock(api.url)
.get('/fields', undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(200, {
data: [
{
data: {
...fieldMock,
},
},
],
pagination: {
offset: 0,
limit: limit,
},
})
.post(
'/fields',
{
name: fieldMock.name,
slug: fieldMock.slug,
type: fieldMock.type,
entities: fieldMock.entities,
config: fieldMock.config,
},
{
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
},
)
.reply(200, {
data: {
...fieldMock,
},
})
.get(`/fields/${fieldId}`, undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(200, {
data: {
...fieldMock,
},
})
.delete(`/fields/${fieldId}`, undefined, {
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
})
.reply(204)
.patch(
`/fields/${fieldId}`,
[
{
op: 'replace',
path: '/name',
value: fieldMock.name,
},
],
{
reqheaders: {
Authorization: `Bearer ${api.token}`,
},
},
)
.reply(200, {
data: {
...fieldMock,
},
});
});

afterAll(() => {
scope.done();
});

it('List fields', async () => {
const fields = await api.listFields();
expect(fields.data.length).toBe(1);
expect(fields.data[0].data.id).toBe(fieldId);
expect(fields.pagination.limit).toBe(limit);
});

it('Add field', async () => {
const field = await api.addField({
name: fieldMock.name,
slug: fieldMock.slug,
type: 'text',
entities: ['project', 'user'],
config: {
locations: [
{
place: 'projectHeader',
},
],
},
});
expect(field.data.id).toBe(fieldId);
});

it('Get field', async () => {
const field = await api.getField(fieldId);
expect(field.data.id).toBe(fieldId);
});

it('Delete field', async () => {
await api.deleteField(fieldId);
});

it('Edit field', async () => {
const field = await api.editField(fieldId, [
{
op: 'replace',
path: '/name',
value: fieldMock.name,
},
]);
expect(field.data.id).toBe(fieldId);
expect(field.data.name).toBe(fieldMock.name);
expect(field.data.slug).toBe(fieldMock.slug);
expect(field.data.type).toBe(fieldMock.type);
expect(field.data.entities).toEqual(fieldMock.entities);
expect(field.data.config).toEqual(fieldMock.config);
});
});
Loading