complexqa_frontend_core 1.24.1 → 1.25.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.
- package/package.json +1 -1
- package/publish/api/attachment_api.js +55 -0
- package/publish/api/file_app_api.js +22 -0
- package/publish/api/index.js +2 -0
- package/publish/app_configuration/table/helpers/table_element_models.js +2 -0
- package/publish/index.js +1 -0
- package/publish/services/FileAppService.js +67 -21
- package/publish/types/element_translate/typeAttachment.i18n.js +201 -0
- package/publish/types/family_elements/typeAttachment.js +119 -0
- package/publish/types/family_events/familyEvent.js +1 -0
- package/publish/types/family_files/typeFileScreenshot.js +2 -2
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ApiAbstractElementClass } from './api_abstract_element_class.js';
|
|
2
|
+
import { typeAttachment } from '../types/family_elements/typeAttachment.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CRUD / Search / delete_file для attachment.
|
|
6
|
+
*
|
|
7
|
+
* @version v.0.1 (13/08/2026)
|
|
8
|
+
*/
|
|
9
|
+
export class AttachmentApi extends ApiAbstractElementClass
|
|
10
|
+
{
|
|
11
|
+
module_prefix = 'attachment';
|
|
12
|
+
|
|
13
|
+
constructor(options)
|
|
14
|
+
{
|
|
15
|
+
super(options);
|
|
16
|
+
this.set_element_class_instance(typeAttachment);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Delete file in File App and unlink all attachments for that file_uuid.
|
|
22
|
+
*
|
|
23
|
+
* POST /web_api/attachment/delete_file
|
|
24
|
+
*
|
|
25
|
+
* @param {object} payload
|
|
26
|
+
* @param {string} payload.file_uuid
|
|
27
|
+
* @returns {Promise<{response: *, payload: *}>}
|
|
28
|
+
*/
|
|
29
|
+
async delete_file(payload)
|
|
30
|
+
{
|
|
31
|
+
if (!this.module_prefix)
|
|
32
|
+
{
|
|
33
|
+
throw new Error('Error #attachment-delete-file-1152 in AttachmentApi');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (this.stage === 'development')
|
|
37
|
+
{
|
|
38
|
+
return {
|
|
39
|
+
response: payload,
|
|
40
|
+
payload : payload,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let url = `/${ this.api_prefix }/${ this.module_prefix }/delete_file`;
|
|
45
|
+
|
|
46
|
+
payload = this.handle_request_payload(payload);
|
|
47
|
+
let response = this.api_request(url, payload);
|
|
48
|
+
response = this.handle_response(response);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
response: response,
|
|
52
|
+
payload : payload,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -114,4 +114,26 @@ export class FileAppApi extends ApiAbstractClass
|
|
|
114
114
|
payload : payload,
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* POST /web_api/file_app/delete_file
|
|
121
|
+
*
|
|
122
|
+
* @param {object} payload
|
|
123
|
+
* @param {string} payload.file_uuid
|
|
124
|
+
*
|
|
125
|
+
* @returns {Promise<{payload: object, response: object}>}
|
|
126
|
+
*/
|
|
127
|
+
async delete_file(payload)
|
|
128
|
+
{
|
|
129
|
+
const url = `/${ this.api_prefix }/${ this.module_prefix }/delete_file`;
|
|
130
|
+
payload = this.handle_request_payload(payload);
|
|
131
|
+
let response = this.api_request(url, payload);
|
|
132
|
+
response = await this.handle_response(response, false);
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
response: response,
|
|
136
|
+
payload : payload,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
117
139
|
}
|
package/publish/api/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { TaskApi } from "./task_api.js";
|
|
|
13
13
|
import { TestAccountApi } from "./test_account_api.js";
|
|
14
14
|
import { BugApi } from "./bug_api.js";
|
|
15
15
|
import { BugRelationApi } from "./bug_relation_api.js";
|
|
16
|
+
import { AttachmentApi } from "./attachment_api.js";
|
|
16
17
|
import { TagApi } from "./tag_api.js";
|
|
17
18
|
import { TagRelationApi } from "./tag_relation_api.js";
|
|
18
19
|
import { TagColorApi } from "./tag/tag_color_api.js";
|
|
@@ -170,6 +171,7 @@ export class Api
|
|
|
170
171
|
this.test_account = new TestAccountApi(payload);
|
|
171
172
|
this.bug = new BugApi(payload);
|
|
172
173
|
this.bug_relation = new BugRelationApi(payload);
|
|
174
|
+
this.attachment = new AttachmentApi(payload);
|
|
173
175
|
this.tag = new TagApi(payload);
|
|
174
176
|
this.tag_relation = new TagRelationApi(payload);
|
|
175
177
|
this.event = new EventApi(payload);
|
|
@@ -4,6 +4,7 @@ import { typeTestRun } from '../../../types/family_elements/typeTestRun.js';
|
|
|
4
4
|
import { typeBug } from '../../../types/family_elements/typeBug.js';
|
|
5
5
|
import { typeBugRelation } from '../../../types/family_relation/typeBugRelation.js';
|
|
6
6
|
import { typeTag } from '../../../types/family_elements/typeTag.js';
|
|
7
|
+
import { typeAttachment } from '../../../types/family_elements/typeAttachment.js';
|
|
7
8
|
import { typeTagRelation } from '../../../types/family_relation/typeTagRelation.js';
|
|
8
9
|
import { typeTestRunResult } from '../../../types/family_elements/typeTestRunResult.js';
|
|
9
10
|
import { typeTestAccount } from '../../../types/family_elements/typeTestAccount.js';
|
|
@@ -33,6 +34,7 @@ export const TABLE_ELEMENT_MODELS = {
|
|
|
33
34
|
test_run : typeTestRun,
|
|
34
35
|
bug : typeBug,
|
|
35
36
|
bug_relation : typeBugRelation,
|
|
37
|
+
attachment : typeAttachment,
|
|
36
38
|
tag : typeTag,
|
|
37
39
|
tag_relation : typeTagRelation,
|
|
38
40
|
test_run_result: typeTestRunResult,
|
package/publish/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { typeTestRunResult } from './types/family_elements/typeTestRunResult.js'
|
|
|
17
17
|
export { typeTestAccount } from './types/family_elements/typeTestAccount.js'
|
|
18
18
|
export {typeBug} from './types/family_elements/typeBug.js';
|
|
19
19
|
export { typeTag } from './types/family_elements/typeTag.js';
|
|
20
|
+
export { typeAttachment } from './types/family_elements/typeAttachment.js';
|
|
20
21
|
|
|
21
22
|
export { familyRelation } from './types/core/1_familyRelation.js';
|
|
22
23
|
export { typeBugRelation } from './types/family_relation/typeBugRelation.js';
|
|
@@ -75,13 +75,53 @@ export class FileAppService extends abstractService
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Whether entity_type is present for File App binding.
|
|
80
|
+
* Empty / null / 0 → omit from token and upload init.
|
|
81
|
+
*
|
|
82
|
+
* @param {*} value
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
static #has_entity_type(value)
|
|
86
|
+
{
|
|
87
|
+
if (value === null || value === undefined)
|
|
88
|
+
{
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const s = String(value).trim();
|
|
93
|
+
|
|
94
|
+
return s !== '' && s !== '0';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Whether entity_id is present for File App binding.
|
|
100
|
+
* Empty / null / 0 / NaN → omit from token and upload init.
|
|
101
|
+
*
|
|
102
|
+
* @param {*} value
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
static #has_entity_id(value)
|
|
106
|
+
{
|
|
107
|
+
if (value === null || value === undefined || value === '')
|
|
108
|
+
{
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const n = Number(value);
|
|
113
|
+
|
|
114
|
+
return !Number.isNaN(n) && n !== 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
78
118
|
/**
|
|
79
119
|
* Upload browser File/Blob to File App (token via Main App bridge).
|
|
80
120
|
*
|
|
81
121
|
* @param {object} payload
|
|
82
122
|
* @param {File|Blob} payload.file
|
|
83
|
-
* @param {string} payload.entity_type
|
|
84
|
-
* @param {number} payload.entity_id
|
|
123
|
+
* @param {string} [payload.entity_type] — omit / null / 0 for unbound upload (attachments)
|
|
124
|
+
* @param {number} [payload.entity_id] — omit / null / 0 for unbound upload (attachments)
|
|
85
125
|
* @param {string} payload.category
|
|
86
126
|
* @param {string} [payload.original_name]
|
|
87
127
|
* @param {string} [payload.file_uuid] — re-upload / new version for existing file
|
|
@@ -97,20 +137,24 @@ export class FileAppService extends abstractService
|
|
|
97
137
|
throw new Error('FileAppService.upload_file: file is required');
|
|
98
138
|
}
|
|
99
139
|
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
140
|
+
const has_entity_type = FileAppService.#has_entity_type(payload.entity_type);
|
|
141
|
+
const has_entity_id = FileAppService.#has_entity_id(payload.entity_id);
|
|
142
|
+
const entity_type = has_entity_type ? String(payload.entity_type) : undefined;
|
|
143
|
+
const entity_id = has_entity_id ? Number(payload.entity_id) : undefined;
|
|
144
|
+
const category = String(payload.category || '');
|
|
145
|
+
const mime_type = file.type || 'application/octet-stream';
|
|
146
|
+
const size_bytes = Number(file.size || 0);
|
|
147
|
+
const original_name = String(payload.original_name || file.name || 'upload');
|
|
148
|
+
const api = FileAppService.#get_api_service();
|
|
149
|
+
|
|
150
|
+
const token_payload = {
|
|
151
|
+
scopes: [ 'file:upload', 'file:read' ],
|
|
112
152
|
size_bytes,
|
|
113
|
-
|
|
153
|
+
...(has_entity_type ? { entity_type } : {}),
|
|
154
|
+
...(has_entity_id ? { entity_id } : {}),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const token_wrap = await api.file_app.issue_access_token(token_payload);
|
|
114
158
|
|
|
115
159
|
const token_data = await token_wrap?.response;
|
|
116
160
|
|
|
@@ -130,15 +174,17 @@ export class FileAppService extends abstractService
|
|
|
130
174
|
throw new ApiException({ message: 'File App access token was not issued' });
|
|
131
175
|
}
|
|
132
176
|
|
|
133
|
-
const
|
|
177
|
+
const init_payload = {
|
|
134
178
|
category,
|
|
135
179
|
mime_type,
|
|
136
180
|
size_bytes,
|
|
137
181
|
original_name,
|
|
138
|
-
entity_type,
|
|
139
|
-
entity_id,
|
|
182
|
+
...(has_entity_type ? { entity_type } : {}),
|
|
183
|
+
...(has_entity_id ? { entity_id } : {}),
|
|
140
184
|
...(payload.file_uuid ? { file_uuid: String(payload.file_uuid) } : {}),
|
|
141
|
-
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const init_response = await axios.post(`${ file_app_url }/api/v1/uploads`, init_payload, {
|
|
142
188
|
headers: {
|
|
143
189
|
Authorization: `Bearer ${ access_token }`,
|
|
144
190
|
Accept : 'application/json',
|
|
@@ -183,8 +229,8 @@ export class FileAppService extends abstractService
|
|
|
183
229
|
const wire = {
|
|
184
230
|
...confirmed,
|
|
185
231
|
category : confirmed.category || category,
|
|
186
|
-
entity_type: confirmed.entity_type
|
|
187
|
-
entity_id : confirmed.entity_id ?? entity_id,
|
|
232
|
+
entity_type: confirmed.entity_type ?? entity_type ?? null,
|
|
233
|
+
entity_id : confirmed.entity_id ?? entity_id ?? null,
|
|
188
234
|
mime_type : confirmed.mime_type || mime_type,
|
|
189
235
|
};
|
|
190
236
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Переводы для typeAttachment.
|
|
3
|
+
*
|
|
4
|
+
* @see typeAttachment
|
|
5
|
+
*/
|
|
6
|
+
export const typeAttachment_i18n = {
|
|
7
|
+
attribute_name_translate_matrix: {
|
|
8
|
+
en: {
|
|
9
|
+
attachment_id: 'ID',
|
|
10
|
+
team_id: 'Team',
|
|
11
|
+
project_id: 'Project',
|
|
12
|
+
element_type: 'Element type',
|
|
13
|
+
element_id: 'Element',
|
|
14
|
+
file_uuid: 'File',
|
|
15
|
+
caption: 'Caption',
|
|
16
|
+
sort_order: 'Sort order',
|
|
17
|
+
user_id: 'User',
|
|
18
|
+
created_at: 'Created',
|
|
19
|
+
updated_at: 'Updated',
|
|
20
|
+
file: 'File',
|
|
21
|
+
},
|
|
22
|
+
ru: {
|
|
23
|
+
attachment_id: 'ID',
|
|
24
|
+
team_id: 'Команда',
|
|
25
|
+
project_id: 'Проект',
|
|
26
|
+
element_type: 'Тип элемента',
|
|
27
|
+
element_id: 'Элемент',
|
|
28
|
+
file_uuid: 'Файл',
|
|
29
|
+
caption: 'Подпись',
|
|
30
|
+
sort_order: 'Порядок',
|
|
31
|
+
user_id: 'Пользователь',
|
|
32
|
+
created_at: 'Создан',
|
|
33
|
+
updated_at: 'Обновлён',
|
|
34
|
+
file: 'Файл',
|
|
35
|
+
},
|
|
36
|
+
de: {
|
|
37
|
+
attachment_id: 'ID',
|
|
38
|
+
team_id: 'Team',
|
|
39
|
+
project_id: 'Projekt',
|
|
40
|
+
element_type: 'Elementtyp',
|
|
41
|
+
element_id: 'Element',
|
|
42
|
+
file_uuid: 'Datei',
|
|
43
|
+
caption: 'Beschriftung',
|
|
44
|
+
sort_order: 'Sortierung',
|
|
45
|
+
user_id: 'Benutzer',
|
|
46
|
+
created_at: 'Erstellt',
|
|
47
|
+
updated_at: 'Aktualisiert',
|
|
48
|
+
file: 'Datei',
|
|
49
|
+
},
|
|
50
|
+
ar: {
|
|
51
|
+
attachment_id: 'ID',
|
|
52
|
+
team_id: 'الفريق',
|
|
53
|
+
project_id: 'المشروع',
|
|
54
|
+
element_type: 'نوع العنصر',
|
|
55
|
+
element_id: 'العنصر',
|
|
56
|
+
file_uuid: 'الملف',
|
|
57
|
+
caption: 'التسمية',
|
|
58
|
+
sort_order: 'ترتيب الفرز',
|
|
59
|
+
user_id: 'المستخدم',
|
|
60
|
+
created_at: 'تاريخ الإنشاء',
|
|
61
|
+
updated_at: 'تاريخ التحديث',
|
|
62
|
+
file: 'الملف',
|
|
63
|
+
},
|
|
64
|
+
es: {
|
|
65
|
+
attachment_id: 'ID',
|
|
66
|
+
team_id: 'Equipo',
|
|
67
|
+
project_id: 'Proyecto',
|
|
68
|
+
element_type: 'Tipo de elemento',
|
|
69
|
+
element_id: 'Elemento',
|
|
70
|
+
file_uuid: 'Archivo',
|
|
71
|
+
caption: 'Leyenda',
|
|
72
|
+
sort_order: 'Orden',
|
|
73
|
+
user_id: 'Usuario',
|
|
74
|
+
created_at: 'Creado',
|
|
75
|
+
updated_at: 'Actualizado',
|
|
76
|
+
file: 'Archivo',
|
|
77
|
+
},
|
|
78
|
+
fr: {
|
|
79
|
+
attachment_id: 'ID',
|
|
80
|
+
team_id: 'Équipe',
|
|
81
|
+
project_id: 'Projet',
|
|
82
|
+
element_type: 'Type d\'élément',
|
|
83
|
+
element_id: 'Élément',
|
|
84
|
+
file_uuid: 'Fichier',
|
|
85
|
+
caption: 'Légende',
|
|
86
|
+
sort_order: 'Ordre',
|
|
87
|
+
user_id: 'Utilisateur',
|
|
88
|
+
created_at: 'Créé',
|
|
89
|
+
updated_at: 'Mis à jour',
|
|
90
|
+
file: 'Fichier',
|
|
91
|
+
},
|
|
92
|
+
hy: {
|
|
93
|
+
attachment_id: 'ID',
|
|
94
|
+
team_id: 'Թիմ',
|
|
95
|
+
project_id: 'Նախագիծ',
|
|
96
|
+
element_type: 'Էլեմենտի տեսակ',
|
|
97
|
+
element_id: 'Էլեմենտ',
|
|
98
|
+
file_uuid: 'Ֆայլ',
|
|
99
|
+
caption: 'Ստորագրություն',
|
|
100
|
+
sort_order: 'Դասավորություն',
|
|
101
|
+
user_id: 'Օգտատեր',
|
|
102
|
+
created_at: 'Ստեղծված',
|
|
103
|
+
updated_at: 'Թարմացված',
|
|
104
|
+
file: 'Ֆայլ',
|
|
105
|
+
},
|
|
106
|
+
it: {
|
|
107
|
+
attachment_id: 'ID',
|
|
108
|
+
team_id: 'Team',
|
|
109
|
+
project_id: 'Progetto',
|
|
110
|
+
element_type: 'Tipo di elemento',
|
|
111
|
+
element_id: 'Elemento',
|
|
112
|
+
file_uuid: 'File',
|
|
113
|
+
caption: 'Didascalia',
|
|
114
|
+
sort_order: 'Ordine',
|
|
115
|
+
user_id: 'Utente',
|
|
116
|
+
created_at: 'Creato',
|
|
117
|
+
updated_at: 'Aggiornato',
|
|
118
|
+
file: 'File',
|
|
119
|
+
},
|
|
120
|
+
pl: {
|
|
121
|
+
attachment_id: 'ID',
|
|
122
|
+
team_id: 'Zespół',
|
|
123
|
+
project_id: 'Projekt',
|
|
124
|
+
element_type: 'Typ elementu',
|
|
125
|
+
element_id: 'Element',
|
|
126
|
+
file_uuid: 'Plik',
|
|
127
|
+
caption: 'Podpis',
|
|
128
|
+
sort_order: 'Kolejność',
|
|
129
|
+
user_id: 'Użytkownik',
|
|
130
|
+
created_at: 'Utworzono',
|
|
131
|
+
updated_at: 'Zaktualizowano',
|
|
132
|
+
file: 'Plik',
|
|
133
|
+
},
|
|
134
|
+
pt: {
|
|
135
|
+
attachment_id: 'ID',
|
|
136
|
+
team_id: 'Equipe',
|
|
137
|
+
project_id: 'Projeto',
|
|
138
|
+
element_type: 'Tipo de elemento',
|
|
139
|
+
element_id: 'Elemento',
|
|
140
|
+
file_uuid: 'Arquivo',
|
|
141
|
+
caption: 'Legenda',
|
|
142
|
+
sort_order: 'Ordem',
|
|
143
|
+
user_id: 'Usuário',
|
|
144
|
+
created_at: 'Criado',
|
|
145
|
+
updated_at: 'Atualizado',
|
|
146
|
+
file: 'Arquivo',
|
|
147
|
+
},
|
|
148
|
+
tt: {
|
|
149
|
+
attachment_id: 'ID',
|
|
150
|
+
team_id: 'Команда',
|
|
151
|
+
project_id: 'Проект',
|
|
152
|
+
element_type: 'Элемент тибы',
|
|
153
|
+
element_id: 'Элемент',
|
|
154
|
+
file_uuid: 'Файл',
|
|
155
|
+
caption: 'Язма',
|
|
156
|
+
sort_order: 'Тәртип',
|
|
157
|
+
user_id: 'Кулланучы',
|
|
158
|
+
created_at: 'Ясалган',
|
|
159
|
+
updated_at: 'Яңартылган',
|
|
160
|
+
file: 'Файл',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
enum_value_translate_matrix: {
|
|
165
|
+
element_type: {
|
|
166
|
+
en: {
|
|
167
|
+
bug: 'Bug',
|
|
168
|
+
},
|
|
169
|
+
ru: {
|
|
170
|
+
bug: 'Баг',
|
|
171
|
+
},
|
|
172
|
+
de: {
|
|
173
|
+
bug: 'Fehler',
|
|
174
|
+
},
|
|
175
|
+
ar: {
|
|
176
|
+
bug: 'الخلل',
|
|
177
|
+
},
|
|
178
|
+
es: {
|
|
179
|
+
bug: 'Defecto',
|
|
180
|
+
},
|
|
181
|
+
fr: {
|
|
182
|
+
bug: 'Anomalie',
|
|
183
|
+
},
|
|
184
|
+
hy: {
|
|
185
|
+
bug: 'Խաուխ',
|
|
186
|
+
},
|
|
187
|
+
it: {
|
|
188
|
+
bug: 'Difetto',
|
|
189
|
+
},
|
|
190
|
+
pl: {
|
|
191
|
+
bug: 'Błąd',
|
|
192
|
+
},
|
|
193
|
+
pt: {
|
|
194
|
+
bug: 'Defeito',
|
|
195
|
+
},
|
|
196
|
+
tt: {
|
|
197
|
+
bug: 'Хата',
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { familyGeneralElement } from "../core/0_familyGeneralElement.js";
|
|
2
|
+
import { is_object } from "../../utils/utils.js";
|
|
3
|
+
import _ from 'underscore';
|
|
4
|
+
import { typeAttachment_i18n } from "../element_translate/typeAttachment.i18n.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* First-class link entity: element ↔ File App file (`file_uuid`).
|
|
8
|
+
*
|
|
9
|
+
* Not a family_relation — polymorphic target via `element_type` + `element_id`,
|
|
10
|
+
* file identity via public `file_uuid` (not File App internal `file_id`).
|
|
11
|
+
*
|
|
12
|
+
* @see docs/Attachments/attachment-docs.MD
|
|
13
|
+
*
|
|
14
|
+
* @version v.0.1 (13/08/2026)
|
|
15
|
+
*/
|
|
16
|
+
export class typeAttachment extends familyGeneralElement
|
|
17
|
+
{
|
|
18
|
+
attachment_id;
|
|
19
|
+
team_id;
|
|
20
|
+
project_id;
|
|
21
|
+
element_type;
|
|
22
|
+
element_id;
|
|
23
|
+
file_uuid;
|
|
24
|
+
caption;
|
|
25
|
+
sort_order;
|
|
26
|
+
user_id;
|
|
27
|
+
created_at;
|
|
28
|
+
updated_at;
|
|
29
|
+
|
|
30
|
+
/** Hydrated via search options.add: ['files'] */
|
|
31
|
+
file;
|
|
32
|
+
|
|
33
|
+
primary_key = 'attachment_id';
|
|
34
|
+
api_key = 'attachment';
|
|
35
|
+
|
|
36
|
+
structure_scheme = {
|
|
37
|
+
attachment_id: 'integer | require',
|
|
38
|
+
team_id : 'integer | require',
|
|
39
|
+
project_id : 'integer | require',
|
|
40
|
+
element_type : 'string | enum | require',
|
|
41
|
+
element_id : 'integer | require',
|
|
42
|
+
file_uuid : 'string | require',
|
|
43
|
+
caption : 'string | optional',
|
|
44
|
+
sort_order : 'integer | optional',
|
|
45
|
+
user_id : 'integer | optional',
|
|
46
|
+
created_at : 'timestamp | optional',
|
|
47
|
+
updated_at : 'timestamp | optional',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
create_scheme = [
|
|
51
|
+
'project_id',
|
|
52
|
+
'element_type',
|
|
53
|
+
'element_id',
|
|
54
|
+
'file_uuid',
|
|
55
|
+
'caption',
|
|
56
|
+
'sort_order',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
available_enum_values = {
|
|
60
|
+
element_type: typeAttachment.get_element_type_values(),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
enum_value_translate_matrix = typeAttachment_i18n.enum_value_translate_matrix;
|
|
64
|
+
|
|
65
|
+
attribute_name_translate_matrix = typeAttachment_i18n.attribute_name_translate_matrix;
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Canonical element_type values (WITHOUT `_id` suffix). MVP: bug only.
|
|
70
|
+
*
|
|
71
|
+
* @returns {string[]}
|
|
72
|
+
*/
|
|
73
|
+
static get_element_type_values()
|
|
74
|
+
{
|
|
75
|
+
return [ 'bug' ];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {object|false|undefined} data
|
|
81
|
+
* @returns {typeAttachment}
|
|
82
|
+
*/
|
|
83
|
+
constructor(data = false)
|
|
84
|
+
{
|
|
85
|
+
super();
|
|
86
|
+
|
|
87
|
+
if (data && is_object(data))
|
|
88
|
+
{
|
|
89
|
+
_.mapObject(data, (value, key) =>
|
|
90
|
+
{
|
|
91
|
+
if (this.hasOwnProperty(key))
|
|
92
|
+
{
|
|
93
|
+
this[ key ] = value;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
get_element_name()
|
|
106
|
+
{
|
|
107
|
+
if (this.caption)
|
|
108
|
+
{
|
|
109
|
+
return String(this.caption);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (this.file_uuid)
|
|
113
|
+
{
|
|
114
|
+
return String(this.file_uuid);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return String(this.attachment_id ?? '');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -21,8 +21,8 @@ export class typeFileScreenshot extends typeFile
|
|
|
21
21
|
*
|
|
22
22
|
* @param {object} payload
|
|
23
23
|
* @param {File|Blob} payload.file
|
|
24
|
-
* @param {string} payload.entity_type
|
|
25
|
-
* @param {number} payload.entity_id
|
|
24
|
+
* @param {string} [payload.entity_type] — omit / null / 0 for unbound upload (attachments)
|
|
25
|
+
* @param {number} [payload.entity_id] — omit / null / 0 for unbound upload (attachments)
|
|
26
26
|
* @param {string} [payload.original_name]
|
|
27
27
|
*
|
|
28
28
|
* @returns {Promise<typeFileScreenshot>}
|