amo-controller 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/classes/Amo.d.ts +50 -0
  4. package/classes/Amo.js +61 -0
  5. package/classes/Companies.d.ts +8 -0
  6. package/classes/Companies.js +8 -0
  7. package/classes/Contacts.d.ts +8 -0
  8. package/classes/Contacts.js +8 -0
  9. package/classes/Events.d.ts +8 -0
  10. package/classes/Events.js +8 -0
  11. package/classes/Leads.d.ts +8 -0
  12. package/classes/Leads.js +8 -0
  13. package/classes/LossReasons.d.ts +8 -0
  14. package/classes/LossReasons.js +8 -0
  15. package/classes/Notes.d.ts +12 -0
  16. package/classes/Notes.js +34 -0
  17. package/classes/Pipelines.d.ts +8 -0
  18. package/classes/Pipelines.js +8 -0
  19. package/classes/Tags.d.ts +10 -0
  20. package/classes/Tags.js +10 -0
  21. package/classes/Tasks.d.ts +8 -0
  22. package/classes/Tasks.js +8 -0
  23. package/classes/Users.d.ts +10 -0
  24. package/classes/Users.js +20 -0
  25. package/classes/base/Entity.d.ts +20 -0
  26. package/classes/base/Entity.js +137 -0
  27. package/classes/base/EntityGetOnly.d.ts +14 -0
  28. package/classes/base/EntityGetOnly.js +71 -0
  29. package/classes/base/EntityWithCodeFields.d.ts +13 -0
  30. package/classes/base/EntityWithCodeFields.js +53 -0
  31. package/index.d.ts +2 -0
  32. package/index.js +2 -0
  33. package/package.json +34 -0
  34. package/tsconfig.tsbuildinfo +1 -0
  35. package/types/company.d.ts +28 -0
  36. package/types/company.js +1 -0
  37. package/types/contact.d.ts +30 -0
  38. package/types/contact.js +1 -0
  39. package/types/custom_fields.d.ts +9 -0
  40. package/types/custom_fields.js +1 -0
  41. package/types/entity.d.ts +44 -0
  42. package/types/entity.js +1 -0
  43. package/types/entity_get_only.d.ts +10 -0
  44. package/types/entity_get_only.js +1 -0
  45. package/types/events.d.ts +18 -0
  46. package/types/events.js +1 -0
  47. package/types/interfaces.d.ts +6 -0
  48. package/types/interfaces.js +1 -0
  49. package/types/lead.d.ts +35 -0
  50. package/types/lead.js +1 -0
  51. package/types/logs.d.ts +3 -0
  52. package/types/logs.js +1 -0
  53. package/types/loss_reason.d.ts +7 -0
  54. package/types/loss_reason.js +1 -0
  55. package/types/note.d.ts +15 -0
  56. package/types/note.js +1 -0
  57. package/types/pipeline.d.ts +22 -0
  58. package/types/pipeline.js +1 -0
  59. package/types/query_params.d.ts +88 -0
  60. package/types/query_params.js +1 -0
  61. package/types/responses.d.ts +21 -0
  62. package/types/responses.js +1 -0
  63. package/types/tag.d.ts +5 -0
  64. package/types/tag.js +1 -0
  65. package/types/task.d.ts +20 -0
  66. package/types/task.js +1 -0
  67. package/types/user.d.ts +64 -0
  68. package/types/user.js +1 -0
  69. package/utils/error.d.ts +2 -0
  70. package/utils/error.js +27 -0
  71. package/utils/logger.d.ts +1 -0
  72. package/utils/logger.js +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Андрей Тулев
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # amo-controller
2
+
3
+ TypeScript-клиент для [AmoCRM API v4](https://www.amocrm.ru/developers/content/crm_platform/api-reference): сделки, контакты, компании, задачи, заметки, воронки, пользователи, теги, события и др. Запросы идут через **axios**, ограничение частоты — **Bottleneck** (по умолчанию до 6 запросов в секунду).
4
+
5
+ ## Установка
6
+
7
+ ```bash
8
+ npm install
9
+ ```
10
+
11
+ ## Сборка
12
+
13
+ Компиляция в каталог `amo/` (см. `tsconfig.json`):
14
+
15
+ ```bash
16
+ npm run build
17
+ ```
18
+
19
+ Точка входа после сборки: `amo/index.js` (поле `main` в `package.json`).
20
+
21
+ ## Использование
22
+
23
+ ```ts
24
+ import Amo from 'amo-controller'
25
+
26
+ const amo = new Amo('https://example.amocrm.ru', 'access_token', {
27
+ rps: 6,
28
+ logs: {
29
+ throwErrors: false,
30
+ // customLogger: myWinstonLogger,
31
+ },
32
+ })
33
+
34
+ const leads = await amo.leads.get({ limit: 50 })
35
+ ```
36
+
37
+ - **`rps`** — целевое число запросов в секунду (лимитер на исходящие вызовы).
38
+ - **`logs.throwErrors`** — если `true`, ошибки HTTP после логирования пробрасываются дальше; иначе методы вроде `get` / `create` / `update` часто возвращают `null`.
39
+ - **`logs.customLogger`** — экземпляр winston вместо встроенного логгера.
40
+
41
+ ## Скрипты
42
+
43
+ | Команда | Назначение |
44
+ | -------------- | ----------------------------- |
45
+ | `npm run build` | Сборка TypeScript → `amo/` |
46
+ | `npm run lint` | Проверка ESLint |
47
+ | `npm run lint:fix` | ESLint с автоисправлением |
48
+ | `npm run format` | Prettier |
49
+ | `npm test` | Запуск `index.test.ts` (нужен `.env`) |
50
+
51
+ ## Локальная проверка API (`npm test`)
52
+
53
+ В корне создайте файл `.env`:
54
+
55
+ ```env
56
+ API_URL=https://ваш-поддомен.amocrm.ru
57
+ API_TOKEN=долгосрочный_токен
58
+ ```
59
+
60
+ Скрипт `index.test.ts` обращается к реальному API; используйте только на тестовом аккаунте.
61
+
62
+ ## Разработка
63
+
64
+ - Исходники: `classes/`, `types/`, `utils/`.
65
+ - Статический анализ: **ESLint** (`eslint.config.mjs`) + **TypeScript** в режиме `strict`.
66
+
67
+ ## Лицензия
68
+
69
+ ISC
@@ -0,0 +1,50 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import Leads from './Leads';
3
+ import Companies from './Companies';
4
+ import Contacts from './Contacts';
5
+ import Users from './Users';
6
+ import Pipelines from './Pipelines';
7
+ import LossReasons from './LossReasons';
8
+ import Notes from './Notes';
9
+ import Tags from './Tags';
10
+ import Events from './Events';
11
+ import Tasks from './Tasks';
12
+ import { Logger } from '../types/logs';
13
+ export default class Amo {
14
+ private baseURL;
15
+ private token;
16
+ readonly options?: {
17
+ rps?: number;
18
+ logs?: {
19
+ customLogger?: Logger;
20
+ throwErrors?: boolean;
21
+ };
22
+ } | undefined;
23
+ instance: AxiosInstance;
24
+ private limiter;
25
+ constructor(baseURL: string, token: string, options?: {
26
+ rps?: number;
27
+ logs?: {
28
+ customLogger?: Logger;
29
+ throwErrors?: boolean;
30
+ };
31
+ } | undefined);
32
+ leads: Leads;
33
+ contacts: Contacts;
34
+ companies: Companies;
35
+ notes: {
36
+ leads: Notes<"leads">;
37
+ contacts: Notes<"contacts">;
38
+ companies: Notes<"companies">;
39
+ };
40
+ tasks: Tasks;
41
+ pipelines: Pipelines;
42
+ users: Users;
43
+ loss_reasons: LossReasons;
44
+ tags: {
45
+ leads: Tags<"leads">;
46
+ contacts: Tags<"contacts">;
47
+ companies: Tags<"companies">;
48
+ };
49
+ events: Events;
50
+ }
package/classes/Amo.js ADDED
@@ -0,0 +1,61 @@
1
+ import axios from 'axios';
2
+ import Bottleneck from 'bottleneck';
3
+ import Leads from './Leads';
4
+ import Companies from './Companies';
5
+ import Contacts from './Contacts';
6
+ import Users from './Users';
7
+ import Pipelines from './Pipelines';
8
+ import LossReasons from './LossReasons';
9
+ import Notes from './Notes';
10
+ import Tags from './Tags';
11
+ import Events from './Events';
12
+ import Tasks from './Tasks';
13
+ import qs from 'qs';
14
+ export default class Amo {
15
+ baseURL;
16
+ token;
17
+ options;
18
+ instance;
19
+ limiter;
20
+ constructor(baseURL, token, options) {
21
+ this.baseURL = baseURL;
22
+ this.token = token;
23
+ this.options = options;
24
+ const rps = this.options?.rps || 6;
25
+ this.limiter = new Bottleneck({
26
+ minTime: Math.ceil(1000 / rps),
27
+ maxConcurrent: rps,
28
+ });
29
+ this.instance = axios.create({
30
+ baseURL: this.baseURL,
31
+ headers: {
32
+ Authorization: `Bearer ${this.token}`,
33
+ 'Content-Type': 'application/json',
34
+ },
35
+ paramsSerializer: (params) => {
36
+ return qs.stringify(params);
37
+ },
38
+ });
39
+ this.instance.interceptors.request.use(async (config) => {
40
+ return this.limiter.schedule(async () => config);
41
+ });
42
+ }
43
+ leads = new Leads(this);
44
+ contacts = new Contacts(this);
45
+ companies = new Companies(this);
46
+ notes = {
47
+ leads: new Notes(this, 'leads'),
48
+ contacts: new Notes(this, 'contacts'),
49
+ companies: new Notes(this, 'companies'),
50
+ };
51
+ tasks = new Tasks(this);
52
+ pipelines = new Pipelines(this);
53
+ users = new Users(this);
54
+ loss_reasons = new LossReasons(this);
55
+ tags = {
56
+ leads: new Tags(this, 'leads'),
57
+ contacts: new Tags(this, 'contacts'),
58
+ companies: new Tags(this, 'companies'),
59
+ };
60
+ events = new Events(this);
61
+ }
@@ -0,0 +1,8 @@
1
+ import Amo from './Amo';
2
+ import { Company } from '../types/company';
3
+ import { DefaultQueryParams } from '../types/query_params';
4
+ import EntityWithCodeFields from './base/EntityWithCodeFields';
5
+ export default class Companies extends EntityWithCodeFields<'companies', Company, DefaultQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import EntityWithCodeFields from './base/EntityWithCodeFields';
2
+ export default class Companies extends EntityWithCodeFields {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'companies');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import Amo from './Amo';
2
+ import { Contact } from '../types/contact';
3
+ import { DefaultQueryParams } from '../types/query_params';
4
+ import EntityWithCodeFields from './base/EntityWithCodeFields';
5
+ export default class Contacts extends EntityWithCodeFields<'contacts', Contact, DefaultQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import EntityWithCodeFields from './base/EntityWithCodeFields';
2
+ export default class Contacts extends EntityWithCodeFields {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'contacts');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { EventQueryParams } from './../types/query_params';
2
+ import { Event } from './../types/events';
3
+ import Amo from './Amo';
4
+ import EntityGetOnly from './base/EntityGetOnly.js';
5
+ export default class Events extends EntityGetOnly<'events', Event, EventQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import EntityGetOnly from './base/EntityGetOnly.js';
2
+ export default class Events extends EntityGetOnly {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'events');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import Amo from './Amo';
2
+ import { Lead } from '../types/lead';
3
+ import Entity from './base/Entity';
4
+ import { DefaultQueryParams } from '../types/query_params';
5
+ export default class Leads extends Entity<'leads', Lead, DefaultQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import Entity from './base/Entity';
2
+ export default class Leads extends Entity {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'leads');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import Amo from './Amo';
2
+ import EntityGetOnly from './base/EntityGetOnly';
3
+ import { LossReason } from '../types/loss_reason';
4
+ import { LossReasonQueryParams } from '../types/query_params';
5
+ export default class LossReasons extends EntityGetOnly<'leads/loss_reasons', LossReason, LossReasonQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import EntityGetOnly from './base/EntityGetOnly';
2
+ export default class LossReasons extends EntityGetOnly {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'leads/loss_reasons');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,12 @@
1
+ import Amo from './Amo';
2
+ import Entity from './base/Entity';
3
+ import { Note } from '../types/note';
4
+ import { SecondEntityType } from '../types/entity';
5
+ import { NoteQueryParams } from '../types/query_params';
6
+ export default class Notes<S extends SecondEntityType> extends Entity<`${S}/notes`, Note, NoteQueryParams> {
7
+ protected amo: Amo;
8
+ protected entity: S;
9
+ constructor(amo: Amo, entity: S);
10
+ pin(noteId: number): Promise<import("axios").AxiosResponse<any, any, {}>> | null;
11
+ unpin(noteId: number): Promise<import("axios").AxiosResponse<any, any, {}>> | null;
12
+ }
@@ -0,0 +1,34 @@
1
+ import Entity from './base/Entity';
2
+ import logError from '../utils/error';
3
+ import { AxiosError } from 'axios';
4
+ export default class Notes extends Entity {
5
+ amo;
6
+ entity;
7
+ constructor(amo, entity) {
8
+ super(amo, `${entity}/notes`);
9
+ this.amo = amo;
10
+ this.entity = entity;
11
+ }
12
+ pin(noteId) {
13
+ try {
14
+ return this.amo.instance.post(`${this.url}/${noteId}/pin`);
15
+ }
16
+ catch (error) {
17
+ logError(`pin ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
18
+ if (this.amo.options?.logs?.throwErrors)
19
+ throw error;
20
+ return null;
21
+ }
22
+ }
23
+ unpin(noteId) {
24
+ try {
25
+ return this.amo.instance.post(`${this.url}/${noteId}/unpin`);
26
+ }
27
+ catch (error) {
28
+ logError(`unpin ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
29
+ if (this.amo.options?.logs?.throwErrors)
30
+ throw error;
31
+ return null;
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ import Amo from './Amo';
2
+ import EntityGetOnly from './base/EntityGetOnly';
3
+ import { Pipeline } from '../types/pipeline';
4
+ import { PipelineQueryParams } from '../types/query_params';
5
+ export default class Pipelines extends EntityGetOnly<'leads/pipelines', Pipeline, PipelineQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import EntityGetOnly from './base/EntityGetOnly';
2
+ export default class Pipelines extends EntityGetOnly {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'leads/pipelines');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ import Amo from './Amo';
2
+ import { SecondEntityType } from '../types/entity';
3
+ import { Tag } from '../types/tag';
4
+ import EntityGetOnly from './base/EntityGetOnly';
5
+ import { TagQueryParams } from '../types/query_params';
6
+ export default class Tags<S extends SecondEntityType> extends EntityGetOnly<`${S}/tags`, Tag, TagQueryParams> {
7
+ protected amo: Amo;
8
+ protected entity: S;
9
+ constructor(amo: Amo, entity: S);
10
+ }
@@ -0,0 +1,10 @@
1
+ import EntityGetOnly from './base/EntityGetOnly';
2
+ export default class Tags extends EntityGetOnly {
3
+ amo;
4
+ entity;
5
+ constructor(amo, entity) {
6
+ super(amo, `${entity}/tags`);
7
+ this.amo = amo;
8
+ this.entity = entity;
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ import Amo from './Amo';
2
+ import { Task } from '../types/task';
3
+ import Entity from './base/Entity';
4
+ import { TaskQueryParams } from '../types/query_params';
5
+ export default class Tasks extends Entity<'tasks', Task, TaskQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ }
@@ -0,0 +1,8 @@
1
+ import Entity from './base/Entity';
2
+ export default class Tasks extends Entity {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'tasks');
6
+ this.amo = amo;
7
+ }
8
+ }
@@ -0,0 +1,10 @@
1
+ import EntityGetOnly from './base/EntityGetOnly';
2
+ import Amo from './Amo';
3
+ import { User } from '../types/user';
4
+ import { UserQueryParams } from '../types/query_params';
5
+ export default class Users extends EntityGetOnly<'users', User, UserQueryParams> {
6
+ protected amo: Amo;
7
+ constructor(amo: Amo);
8
+ getById(id: number): Promise<User | null>;
9
+ getByName(name: string): Promise<User | null>;
10
+ }
@@ -0,0 +1,20 @@
1
+ import EntityGetOnly from './base/EntityGetOnly';
2
+ export default class Users extends EntityGetOnly {
3
+ amo;
4
+ constructor(amo) {
5
+ super(amo, 'users');
6
+ this.amo = amo;
7
+ }
8
+ async getById(id) {
9
+ const users = await this.get();
10
+ if (!users)
11
+ return users;
12
+ return users.find((user) => user.id === id) || null;
13
+ }
14
+ async getByName(name) {
15
+ const users = await this.get();
16
+ if (!users)
17
+ return users;
18
+ return users.find((user) => user.name === name) || null;
19
+ }
20
+ }
@@ -0,0 +1,20 @@
1
+ import { QueryParams } from '../../types/query_params';
2
+ import Amo from '../Amo';
3
+ import { CreateResponse, UpdateResponse } from '../../types/responses';
4
+ import { CustomField } from '../../types/custom_fields';
5
+ import { EntitiesType, EntityClass, EntitiesFields, ExtendableEntitiesForUpdate, PartialExcept, SecondEntityType, ToUpdate } from '../../types/entity';
6
+ export default class Entity<N extends EntitiesType, E extends EntitiesFields, Q extends QueryParams> implements EntityClass<E, Q> {
7
+ protected amo: Amo;
8
+ protected type: N;
9
+ constructor(amo: Amo, type: N);
10
+ readonly url: string;
11
+ readonly limit: number;
12
+ get(params?: Q, page?: number, acc?: E[]): Promise<E[] | null>;
13
+ getCustomFieldById(entity: E, id: number): CustomField | null;
14
+ getNewest(entities: E[], by: 'created_at' | 'updated_at' | 'closed_at'): E;
15
+ getOldest(entities: E[], by: 'created_at' | 'updated_at'): E;
16
+ create(entities: Partial<E>[]): Promise<CreateResponse[] | null>;
17
+ update(entities: (E extends ExtendableEntitiesForUpdate ? PartialExcept<E, 'id'> & ToUpdate : PartialExcept<E, 'id'>)[]): Promise<UpdateResponse[] | null>;
18
+ link(entity: SecondEntityType, id: number, linkIds: number[]): Promise<void>;
19
+ unlink(entity: SecondEntityType, id: number, linkIds: number[]): Promise<void>;
20
+ }
@@ -0,0 +1,137 @@
1
+ import logError from '../../utils/error';
2
+ import { AxiosError } from 'axios';
3
+ export default class Entity {
4
+ amo;
5
+ type;
6
+ constructor(amo, type) {
7
+ this.amo = amo;
8
+ this.type = type;
9
+ this.url = 'api/v4/' + type;
10
+ this.type = type.split('/').at(-1);
11
+ this.limit = 250;
12
+ }
13
+ url;
14
+ limit;
15
+ async get(params = {}, page = 1, acc = []) {
16
+ try {
17
+ const response = await this.amo.instance.get(this.url, {
18
+ params: {
19
+ limit: this.limit,
20
+ page,
21
+ ...params,
22
+ },
23
+ });
24
+ const entity = response.data._embedded?.[this.type];
25
+ if (!entity)
26
+ return acc;
27
+ const result = acc.concat(entity);
28
+ if (entity.length === (params.limit || this.limit) && !params.page) {
29
+ return this.get(params, ++page, result);
30
+ }
31
+ return result;
32
+ }
33
+ catch (error) {
34
+ logError(`get ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
35
+ if (this.amo.options?.logs?.throwErrors)
36
+ throw error;
37
+ return null;
38
+ }
39
+ }
40
+ getCustomFieldById(entity, id) {
41
+ const field = entity.custom_fields_values?.find((field) => {
42
+ if (field.field_id && field.field_id === id)
43
+ return field;
44
+ });
45
+ return field || null;
46
+ }
47
+ getNewest(entities, by) {
48
+ let newest = entities[0];
49
+ for (const entity of entities) {
50
+ const newestBy = newest[by];
51
+ if (!newestBy) {
52
+ newest = entity;
53
+ continue;
54
+ }
55
+ const entityBy = entity[by];
56
+ if (!entityBy)
57
+ continue;
58
+ if (entityBy > newestBy)
59
+ newest = entity;
60
+ }
61
+ return newest;
62
+ }
63
+ getOldest(entities, by) {
64
+ let oldest = entities[0];
65
+ for (const entity of entities) {
66
+ const oldestBy = oldest[by];
67
+ if (!oldestBy) {
68
+ oldest = entity;
69
+ continue;
70
+ }
71
+ const entityBy = entity[by];
72
+ if (!entityBy)
73
+ continue;
74
+ if (entityBy < oldestBy)
75
+ oldest = entity;
76
+ }
77
+ return oldest;
78
+ }
79
+ async create(entities) {
80
+ try {
81
+ const response = await this.amo.instance.post(this.url, entities);
82
+ return response.data._embedded?.[this.type];
83
+ }
84
+ catch (error) {
85
+ logError(`create ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
86
+ if (this.amo.options?.logs?.throwErrors)
87
+ throw error;
88
+ return null;
89
+ }
90
+ }
91
+ async update(entities) {
92
+ try {
93
+ const response = await this.amo.instance.patch(this.url, entities);
94
+ return response.data._embedded?.[this.type];
95
+ }
96
+ catch (error) {
97
+ logError(`update ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
98
+ if (this.amo.options?.logs?.throwErrors)
99
+ throw error;
100
+ return null;
101
+ }
102
+ }
103
+ async link(entity, id, linkIds) {
104
+ try {
105
+ const data = [];
106
+ for (const link of linkIds)
107
+ data.push({
108
+ entity_id: id,
109
+ to_entity_id: link,
110
+ to_entity_type: entity,
111
+ });
112
+ await this.amo.instance.post(this.url + '/link', data);
113
+ }
114
+ catch (error) {
115
+ logError(`link ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
116
+ if (this.amo.options?.logs?.throwErrors)
117
+ throw error;
118
+ }
119
+ }
120
+ async unlink(entity, id, linkIds) {
121
+ try {
122
+ const data = [];
123
+ for (const link of linkIds)
124
+ data.push({
125
+ entity_id: id,
126
+ to_entity_id: link,
127
+ to_entity_type: entity,
128
+ });
129
+ await this.amo.instance.post(this.url + '/unlink', data);
130
+ }
131
+ catch (error) {
132
+ logError(`unlink ${this.type} error`, error, error instanceof AxiosError ? error.response?.data : null, this.amo.options?.logs?.customLogger);
133
+ if (this.amo.options?.logs?.throwErrors)
134
+ throw error;
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,14 @@
1
+ import { GetOnlyParams } from '../../types/query_params';
2
+ import Amo from '../Amo';
3
+ import { EntitiesGetOnlyType, EntityGetOnlyClass } from '../../types/entity_get_only';
4
+ import type { EntitiesFields } from '../../types/entity';
5
+ export default class EntityGetOnly<N extends EntitiesGetOnlyType, E extends EntitiesFields, Q extends GetOnlyParams> implements EntityGetOnlyClass<E, Q> {
6
+ protected amo: Amo;
7
+ protected type: N;
8
+ constructor(amo: Amo, type: N);
9
+ readonly url: string;
10
+ readonly limit: number;
11
+ get(params?: Q, page?: number, acc?: E[]): Promise<E[] | null>;
12
+ getNewest(entities: E[], by: 'created_at' | 'updated_at'): E;
13
+ getOldest(entities: E[], by: 'created_at' | 'updated_at'): E;
14
+ }