@crowdin/app-project-module 0.71.2 → 0.73.0
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/out/middlewares/integration-credentials.js +6 -9
- package/out/modules/integration/handlers/invite-users.d.ts +3 -0
- package/out/modules/integration/handlers/invite-users.js +268 -0
- package/out/modules/integration/handlers/main.js +11 -5
- package/out/modules/integration/handlers/users.d.ts +13 -0
- package/out/modules/integration/handlers/users.js +102 -0
- package/out/modules/integration/index.js +14 -0
- package/out/modules/integration/types.d.ts +1 -0
- package/out/modules/integration/util/defaults.js +0 -48
- package/out/modules/manifest.js +5 -2
- package/out/modules/webhooks/handlers/webhook-handler.js +1 -3
- package/out/modules/webhooks/types.d.ts +1 -1
- package/out/modules/workflow-step-type/types.d.ts +9 -0
- package/out/modules/workflow-step-type/types.js +7 -0
- package/out/static/css/styles.css +77 -0
- package/out/storage/index.d.ts +1 -0
- package/out/storage/mysql.d.ts +1 -0
- package/out/storage/mysql.js +10 -3
- package/out/storage/postgre.d.ts +1 -0
- package/out/storage/postgre.js +10 -3
- package/out/storage/sqlite.d.ts +2 -0
- package/out/storage/sqlite.js +28 -3
- package/out/types.d.ts +4 -2
- package/out/types.js +2 -0
- package/out/util/index.d.ts +1 -0
- package/out/util/index.js +12 -1
- package/out/views/main.handlebars +274 -14
- package/package.json +1 -1
|
@@ -53,7 +53,7 @@ function handle(config, integration, optional = false) {
|
|
|
53
53
|
if (projectIntegrationCredentials.length) {
|
|
54
54
|
for (const credentials of projectIntegrationCredentials) {
|
|
55
55
|
ownerIds.push(crowdinAppFunctions.parseCrowdinId(credentials.id).userId);
|
|
56
|
-
if (
|
|
56
|
+
if (checkUserAccessToIntegration(credentials, `${userId}`)) {
|
|
57
57
|
integrationCredentials = credentials;
|
|
58
58
|
clientId = credentials.id;
|
|
59
59
|
req.crowdinContext.clientId = clientId;
|
|
@@ -99,14 +99,11 @@ function handle(config, integration, optional = false) {
|
|
|
99
99
|
}
|
|
100
100
|
exports.default = handle;
|
|
101
101
|
function checkUserAccessToIntegration(integrationCredentials, userId) {
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
});
|
|
102
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
|
|
103
|
+
const managers = JSON.parse(integrationCredentials.managers);
|
|
104
|
+
return (managers || []).includes(userId);
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
110
107
|
}
|
|
111
108
|
function getIntegrationManagedBy(ownerIds, req) {
|
|
112
109
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { Response } from 'express';
|
|
3
|
+
export default function handle(): (req: import("../../../types").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const util_1 = require("../../../util");
|
|
13
|
+
const types_1 = require("../../../types");
|
|
14
|
+
const storage_1 = require("../../../storage");
|
|
15
|
+
const users_1 = require("./users");
|
|
16
|
+
const CONSTANTS = {
|
|
17
|
+
MAX_EMAIL_LENGTH: 76,
|
|
18
|
+
MANAGER_ROLES: ['owner', 'manager'],
|
|
19
|
+
PROJECT_INTEGRATIONS_MODULE_TYPE: 'project-integrations',
|
|
20
|
+
};
|
|
21
|
+
function handle() {
|
|
22
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const onlyCheck = req.body.onlyCheck;
|
|
24
|
+
const usersToInvite = req.body.users;
|
|
25
|
+
if (!Array.isArray(usersToInvite)) {
|
|
26
|
+
return res.status(400).send('Invalid request');
|
|
27
|
+
}
|
|
28
|
+
const client = req.crowdinApiClient;
|
|
29
|
+
const projectId = req.crowdinContext.jwtPayload.context.project_id;
|
|
30
|
+
const userId = req.crowdinContext.jwtPayload.context.user_id;
|
|
31
|
+
const { projectMembers, organizationMembers } = yield (0, users_1.getUsers)({ client, projectId });
|
|
32
|
+
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId);
|
|
33
|
+
let applicationInstallation = null;
|
|
34
|
+
try {
|
|
35
|
+
applicationInstallation = yield client.applicationsApi.getApplicationInstallation(req.crowdinContext.appIdentifier);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code !== 403) {
|
|
39
|
+
console.error('Failed to get application installation', error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const isAdmin = organizationMembers.some((member) => member.id === +userId && member.isAdmin) ||
|
|
43
|
+
projectMembers.some((member) => member.id === +userId && 'role' in member && member.role === 'owner');
|
|
44
|
+
const hasManagerAccess = isAdmin ||
|
|
45
|
+
projectMembers.some((member) => member.id === +userId &&
|
|
46
|
+
('role' in member ? CONSTANTS.MANAGER_ROLES.includes(member.role) : member.isManager));
|
|
47
|
+
if (!hasManagerAccess) {
|
|
48
|
+
return res.status(403).send({ error: 'Access denied' });
|
|
49
|
+
}
|
|
50
|
+
const usersWhoWillBeInvitedToOrganization = filterOrganizationUsers(usersToInvite);
|
|
51
|
+
const usersWhoWillBeInvitedToProject = filterProjectUsers({
|
|
52
|
+
usersToInvite,
|
|
53
|
+
projectMembers,
|
|
54
|
+
organizationMembers,
|
|
55
|
+
});
|
|
56
|
+
const usersWhoNotIssetInApplicationInstallation = filterNotIssetApplicationUsers({
|
|
57
|
+
usersToInvite,
|
|
58
|
+
applicationInstallation,
|
|
59
|
+
projectMembers,
|
|
60
|
+
organizationMembers,
|
|
61
|
+
});
|
|
62
|
+
const usersWhoNotIssetInIntegration = filterNotIssetIntegrationUsers({
|
|
63
|
+
usersToInvite,
|
|
64
|
+
integrationCredentials,
|
|
65
|
+
projectMembers,
|
|
66
|
+
organizationMembers,
|
|
67
|
+
});
|
|
68
|
+
if (onlyCheck) {
|
|
69
|
+
return res.send({
|
|
70
|
+
data: {
|
|
71
|
+
isAdmin,
|
|
72
|
+
editApplicationAvailable: applicationInstallation !== null,
|
|
73
|
+
usersWhoWillBeInvitedToOrganization,
|
|
74
|
+
usersWhoWillBeInvitedToProject,
|
|
75
|
+
usersWhoNotIssetInApplicationInstallation,
|
|
76
|
+
usersWhoNotIssetInIntegration,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const response = yield inviteUsers({
|
|
81
|
+
req,
|
|
82
|
+
projectId,
|
|
83
|
+
isAdmin,
|
|
84
|
+
usersToInvite,
|
|
85
|
+
applicationInstallation,
|
|
86
|
+
usersWhoWillBeInvitedToOrganization,
|
|
87
|
+
usersWhoWillBeInvitedToProject,
|
|
88
|
+
usersWhoNotIssetInApplicationInstallation,
|
|
89
|
+
});
|
|
90
|
+
return res.send(response);
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
exports.default = handle;
|
|
94
|
+
function filterOrganizationUsers(usersToInvite) {
|
|
95
|
+
return usersToInvite.filter((identifier) => (0, util_1.validateEmail)(identifier)).map((name) => ({ name: `${name}` }));
|
|
96
|
+
}
|
|
97
|
+
function filterProjectUsers({ usersToInvite, projectMembers, organizationMembers, }) {
|
|
98
|
+
return usersToInvite
|
|
99
|
+
.map((identifier) => {
|
|
100
|
+
if ((0, util_1.validateEmail)(identifier)) {
|
|
101
|
+
return { name: `${identifier}` };
|
|
102
|
+
}
|
|
103
|
+
const user = projectMembers.find((member) => member.id === +identifier);
|
|
104
|
+
if (!user) {
|
|
105
|
+
const organizationUser = organizationMembers.find((member) => member.id === +identifier);
|
|
106
|
+
return organizationUser && !organizationUser.isAdmin
|
|
107
|
+
? { id: organizationUser.id, name: (0, users_1.getUserFullName)(organizationUser) }
|
|
108
|
+
: null;
|
|
109
|
+
}
|
|
110
|
+
return ('role' in user ? !CONSTANTS.MANAGER_ROLES.includes(user.role) : !user.isManager)
|
|
111
|
+
? { id: user.id, name: (0, users_1.getUserFullName)(user) }
|
|
112
|
+
: null;
|
|
113
|
+
})
|
|
114
|
+
.filter(Boolean);
|
|
115
|
+
}
|
|
116
|
+
function filterNotIssetApplicationUsers({ usersToInvite, applicationInstallation, projectMembers, organizationMembers, }) {
|
|
117
|
+
let userIdentifiers = [];
|
|
118
|
+
if (!applicationInstallation) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const projectIntegrationModules = applicationInstallation.data.modules.filter((module) => module.type === CONSTANTS.PROJECT_INTEGRATIONS_MODULE_TYPE);
|
|
122
|
+
if (!projectIntegrationModules.length) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
if (projectIntegrationModules.some((module) => module.permissions.user.value === types_1.UserPermissions.ALL_MEMBERS)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
userIdentifiers = usersToInvite.filter((userId) => projectIntegrationModules.every((module) => !module.permissions.user.ids.includes(+userId)));
|
|
130
|
+
}
|
|
131
|
+
return userIdentifiers
|
|
132
|
+
.map((identifier) => {
|
|
133
|
+
if ((0, util_1.validateEmail)(identifier)) {
|
|
134
|
+
return { name: `${identifier}` };
|
|
135
|
+
}
|
|
136
|
+
let user;
|
|
137
|
+
user = projectMembers.find((member) => member.id === +identifier);
|
|
138
|
+
if (!user) {
|
|
139
|
+
user = organizationMembers.find((member) => member.id === +identifier);
|
|
140
|
+
}
|
|
141
|
+
return user ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null;
|
|
142
|
+
})
|
|
143
|
+
.filter(Boolean);
|
|
144
|
+
}
|
|
145
|
+
function filterNotIssetIntegrationUsers({ usersToInvite, integrationCredentials, projectMembers, organizationMembers, }) {
|
|
146
|
+
let integrationManagers = [];
|
|
147
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
|
|
148
|
+
integrationManagers = JSON.parse(integrationCredentials.managers);
|
|
149
|
+
}
|
|
150
|
+
return usersToInvite
|
|
151
|
+
.map((identifier) => {
|
|
152
|
+
if (integrationManagers.includes(`${identifier}`)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
if ((0, util_1.validateEmail)(identifier)) {
|
|
156
|
+
return { name: `${identifier}` };
|
|
157
|
+
}
|
|
158
|
+
let user;
|
|
159
|
+
user = projectMembers.find((member) => member.id === +identifier);
|
|
160
|
+
if (!user) {
|
|
161
|
+
user = organizationMembers.find((member) => member.id === +identifier);
|
|
162
|
+
}
|
|
163
|
+
return user ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null;
|
|
164
|
+
})
|
|
165
|
+
.filter(Boolean);
|
|
166
|
+
}
|
|
167
|
+
function inviteUsers({ req, projectId, isAdmin, usersToInvite, applicationInstallation, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, }) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
const client = req.crowdinApiClient;
|
|
170
|
+
const alreadyAddedUserIds = yield inviteUsersToProject({
|
|
171
|
+
client,
|
|
172
|
+
projectId,
|
|
173
|
+
usersWhoWillBeInvitedToOrganization,
|
|
174
|
+
usersWhoWillBeInvitedToProject,
|
|
175
|
+
});
|
|
176
|
+
if (isAdmin) {
|
|
177
|
+
yield addUsersToApplicationInstallation({
|
|
178
|
+
client,
|
|
179
|
+
ownerId: req.integrationCredentials.ownerId,
|
|
180
|
+
applicationInstallation,
|
|
181
|
+
alreadyAddedUserIds,
|
|
182
|
+
usersWhoNotIssetInApplicationInstallation,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
yield addUsersToIntegration({
|
|
186
|
+
clientId: req.crowdinContext.clientId,
|
|
187
|
+
alreadyAddedUserIds,
|
|
188
|
+
usersToInvite,
|
|
189
|
+
});
|
|
190
|
+
return { success: true };
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function inviteUsersToProject({ client, projectId, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, }) {
|
|
194
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
let addedIds = [];
|
|
196
|
+
const emailInvites = usersWhoWillBeInvitedToOrganization.map((user) => user.name);
|
|
197
|
+
const userIdInvites = usersWhoWillBeInvitedToProject.map((user) => user.id).filter(Boolean);
|
|
198
|
+
try {
|
|
199
|
+
if (emailInvites.length) {
|
|
200
|
+
const inviteResponse = (yield client.usersApi.addProjectMember(projectId, {
|
|
201
|
+
managerAccess: true,
|
|
202
|
+
emails: emailInvites,
|
|
203
|
+
})); // TODO: fix typings in the @crowdin/crowdin-api-client
|
|
204
|
+
addedIds = inviteResponse.data.added.map((member) => member.id);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.error('Failed to invite users', error);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
if (userIdInvites.length) {
|
|
212
|
+
yield client.usersApi.addProjectMember(projectId, {
|
|
213
|
+
managerAccess: true,
|
|
214
|
+
userIds: userIdInvites,
|
|
215
|
+
}); // TODO: fix typings in the @crowdin/crowdin-api-client
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('Failed to grant project manager access', error);
|
|
220
|
+
}
|
|
221
|
+
return addedIds;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function addUsersToApplicationInstallation({ client, ownerId, applicationInstallation, alreadyAddedUserIds, usersWhoNotIssetInApplicationInstallation, }) {
|
|
225
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
if (!applicationInstallation || !usersWhoNotIssetInApplicationInstallation.length) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const applicationInvites = [
|
|
230
|
+
ownerId,
|
|
231
|
+
...alreadyAddedUserIds,
|
|
232
|
+
...usersWhoNotIssetInApplicationInstallation.map((user) => user.id).filter(Boolean),
|
|
233
|
+
];
|
|
234
|
+
const permissions = applicationInstallation.data.modules.filter((module) => module.type === CONSTANTS.PROJECT_INTEGRATIONS_MODULE_TYPE);
|
|
235
|
+
if (!permissions.length) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
for (const module of permissions) {
|
|
240
|
+
if (module.permissions.user.value === types_1.UserPermissions.ALL_MEMBERS) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const userIds = [...new Set([...module.permissions.user.ids, ...applicationInvites])];
|
|
244
|
+
client.applicationsApi.editApplicationInstallation(applicationInstallation.data.identifier, [
|
|
245
|
+
{
|
|
246
|
+
op: 'replace',
|
|
247
|
+
path: `/modules/${module.key}/permissions`,
|
|
248
|
+
value: {
|
|
249
|
+
user: {
|
|
250
|
+
value: types_1.UserPermissions.RESTRICTED,
|
|
251
|
+
ids: userIds,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
]);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error('Failed to update application permissions', error);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function addUsersToIntegration({ clientId, alreadyAddedUserIds, usersToInvite, }) {
|
|
264
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
265
|
+
const integrationInvites = [...alreadyAddedUserIds, ...usersToInvite.filter((identifier) => +identifier)];
|
|
266
|
+
yield (0, storage_1.getStorage)().updateIntegrationManagers(clientId, JSON.stringify(integrationInvites.map((id) => `${id}`)));
|
|
267
|
+
});
|
|
268
|
+
}
|
|
@@ -39,7 +39,7 @@ const defaults_1 = require("../util/defaults");
|
|
|
39
39
|
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
40
40
|
function handle(config, integration) {
|
|
41
41
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
42
|
-
var _a, _b;
|
|
42
|
+
var _a, _b, _c;
|
|
43
43
|
const logger = req.logInfo || logger_1.log;
|
|
44
44
|
const installed = !!req.crowdinApiClient;
|
|
45
45
|
const loggedIn = !!req.integrationCredentials;
|
|
@@ -66,12 +66,18 @@ function handle(config, integration) {
|
|
|
66
66
|
}
|
|
67
67
|
else if (integration.getConfiguration) {
|
|
68
68
|
const { userId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
69
|
-
options.
|
|
70
|
-
|
|
71
|
-
options.configurationFields = configurationFields;
|
|
69
|
+
options.hasOrganization = !!req.crowdinContext.jwtPayload.domain;
|
|
70
|
+
options.isOwner = req.integrationCredentials.ownerId === userId;
|
|
72
71
|
options.config = JSON.stringify(req.integrationSettings || {});
|
|
73
72
|
options.reloadOnConfigSave = !!integration.reloadOnConfigSave;
|
|
74
73
|
options.integrationPagination = integration.integrationPagination;
|
|
74
|
+
if ((_b = req.query) === null || _b === void 0 ? void 0 : _b.parentUrl) {
|
|
75
|
+
const parentUrl = new URL(req.query.parentUrl);
|
|
76
|
+
parentUrl.searchParams.set('zen-mode', 'true');
|
|
77
|
+
options.zenModeUrl = parentUrl.toString();
|
|
78
|
+
}
|
|
79
|
+
const configurationFields = yield integration.getConfiguration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials);
|
|
80
|
+
options.configurationFields = configurationFields;
|
|
75
81
|
logger(`Adding configuration fields ${JSON.stringify(configurationFields, null, 2)}`);
|
|
76
82
|
}
|
|
77
83
|
options.infoModal = integration.infoModal;
|
|
@@ -96,7 +102,7 @@ function handle(config, integration) {
|
|
|
96
102
|
: null;
|
|
97
103
|
options.notice = integration.notice;
|
|
98
104
|
options.asyncProgress = {
|
|
99
|
-
checkInterval: ((
|
|
105
|
+
checkInterval: ((_c = integration.asyncProgress) === null || _c === void 0 ? void 0 : _c.checkInterval) || 1000,
|
|
100
106
|
};
|
|
101
107
|
logger(`Routing user to ${view} view`);
|
|
102
108
|
return res.render(view, options);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import Crowdin, { UsersModel } from '@crowdin/crowdin-api-client';
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
export type ProjectMember = UsersModel.ProjectMember | UsersModel.EnterpriseProjectMember;
|
|
5
|
+
export default function handle(): (req: import("../../../types").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
6
|
+
export declare function getUsers({ client, projectId }: {
|
|
7
|
+
client: Crowdin;
|
|
8
|
+
projectId: number;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
projectMembers: ProjectMember[];
|
|
11
|
+
organizationMembers: UsersModel.User[];
|
|
12
|
+
}>;
|
|
13
|
+
export declare function getUserFullName(user: UsersModel.User | ProjectMember): string;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.getUserFullName = exports.getUsers = void 0;
|
|
36
|
+
const util_1 = require("../../../util");
|
|
37
|
+
const storage_1 = require("../../../storage");
|
|
38
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
39
|
+
function handle() {
|
|
40
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
const client = req.crowdinApiClient;
|
|
42
|
+
const ownerId = req.integrationCredentials.ownerId;
|
|
43
|
+
const { projectId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
44
|
+
const { projectMembers, organizationMembers } = yield getUsers({ client, projectId });
|
|
45
|
+
const uniqueUsers = new Map();
|
|
46
|
+
[...projectMembers, ...organizationMembers].forEach((manager) => {
|
|
47
|
+
if (manager.id !== ownerId && !uniqueUsers.has(manager.id)) {
|
|
48
|
+
uniqueUsers.set(manager.id, manager);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
const sortedUsers = Array.from(uniqueUsers.values())
|
|
52
|
+
.sort((a, b) => {
|
|
53
|
+
const aValue = a.firstName || a.username || '';
|
|
54
|
+
const bValue = b.firstName || b.username || '';
|
|
55
|
+
return aValue.localeCompare(bValue);
|
|
56
|
+
})
|
|
57
|
+
.map((user) => ({
|
|
58
|
+
id: user.id.toString(),
|
|
59
|
+
name: getUserFullName(user),
|
|
60
|
+
}));
|
|
61
|
+
let integrationManagers = [];
|
|
62
|
+
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId);
|
|
63
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
|
|
64
|
+
integrationManagers = JSON.parse(integrationCredentials.managers);
|
|
65
|
+
}
|
|
66
|
+
res.json({
|
|
67
|
+
data: {
|
|
68
|
+
users: sortedUsers,
|
|
69
|
+
managers: integrationManagers,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
exports.default = handle;
|
|
75
|
+
function getUsers({ client, projectId }) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
const users = {
|
|
78
|
+
projectMembers: [],
|
|
79
|
+
organizationMembers: [],
|
|
80
|
+
};
|
|
81
|
+
const projectMembers = (yield client.usersApi.withFetchAll().listProjectMembers(projectId)).data;
|
|
82
|
+
users.projectMembers = projectMembers.map((members) => members.data);
|
|
83
|
+
if (client.organization) {
|
|
84
|
+
try {
|
|
85
|
+
const admins = (yield client.usersApi.withFetchAll().listUsers()).data;
|
|
86
|
+
users.organizationMembers = admins.map((admin) => admin.data);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('Failed to get organization users', error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return users;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
exports.getUsers = getUsers;
|
|
96
|
+
function getUserFullName(user) {
|
|
97
|
+
const ownerFullName = 'fullName' in user ? user.fullName : ((user.firstName || '') + ' ' + (user.lastName || '')).trim();
|
|
98
|
+
return !!ownerFullName && (user === null || user === void 0 ? void 0 : user.username) !== ownerFullName
|
|
99
|
+
? `${ownerFullName} (${user === null || user === void 0 ? void 0 : user.username})`
|
|
100
|
+
: user === null || user === void 0 ? void 0 : user.username;
|
|
101
|
+
}
|
|
102
|
+
exports.getUserFullName = getUserFullName;
|
|
@@ -64,6 +64,8 @@ const subscription_info_1 = __importDefault(require("./handlers/subscription-inf
|
|
|
64
64
|
const sync_settings_1 = __importDefault(require("./handlers/sync-settings"));
|
|
65
65
|
const sync_settings_save_1 = __importDefault(require("./handlers/sync-settings-save"));
|
|
66
66
|
const user_errors_1 = __importDefault(require("./handlers/user-errors"));
|
|
67
|
+
const users_1 = __importDefault(require("./handlers/users"));
|
|
68
|
+
const invite_users_1 = __importDefault(require("./handlers/invite-users"));
|
|
67
69
|
const cron_1 = require("./util/cron");
|
|
68
70
|
const storage_1 = require("../../storage");
|
|
69
71
|
function register({ config, app }) {
|
|
@@ -222,6 +224,18 @@ function register({ config, app }) {
|
|
|
222
224
|
checkSubscriptionExpiration: true,
|
|
223
225
|
moduleKey: integrationLogic.key,
|
|
224
226
|
}), (0, integration_credentials_1.default)(config, integrationLogic), (0, user_errors_1.default)());
|
|
227
|
+
app.get('/api/users', json_response_1.default, (0, crowdin_client_1.default)({
|
|
228
|
+
config,
|
|
229
|
+
optional: false,
|
|
230
|
+
checkSubscriptionExpiration: true,
|
|
231
|
+
moduleKey: integrationLogic.key,
|
|
232
|
+
}), (0, integration_credentials_1.default)(config, integrationLogic), (0, users_1.default)());
|
|
233
|
+
app.post('/api/invite-users', json_response_1.default, (0, crowdin_client_1.default)({
|
|
234
|
+
config,
|
|
235
|
+
optional: false,
|
|
236
|
+
checkSubscriptionExpiration: true,
|
|
237
|
+
moduleKey: integrationLogic.key,
|
|
238
|
+
}), (0, integration_credentials_1.default)(config, integrationLogic), (0, invite_users_1.default)());
|
|
225
239
|
cron.schedule('0 0 1 * *', () => (0, cron_1.removeFinishedJobs)());
|
|
226
240
|
}
|
|
227
241
|
exports.register = register;
|
|
@@ -147,27 +147,6 @@ function applyIntegrationModuleDefaults(config, integration) {
|
|
|
147
147
|
fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
|
|
148
148
|
}
|
|
149
149
|
const defaultSettings = [];
|
|
150
|
-
const mangers = yield getManagers(crowdinClient, projectId, integrationCredentials.ownerId);
|
|
151
|
-
if (mangers.length) {
|
|
152
|
-
defaultSettings.push({
|
|
153
|
-
key: 'managers',
|
|
154
|
-
label: 'Managers',
|
|
155
|
-
type: 'select',
|
|
156
|
-
isMulti: true,
|
|
157
|
-
isSearchable: true,
|
|
158
|
-
options: mangers.map((manager) => {
|
|
159
|
-
const ownerFullName = 'fullName' in manager
|
|
160
|
-
? manager.fullName
|
|
161
|
-
: ((manager.firstName || '') + ' ' + (manager.lastName || '')).trim();
|
|
162
|
-
return {
|
|
163
|
-
value: manager.id.toString(),
|
|
164
|
-
label: !!ownerFullName && (manager === null || manager === void 0 ? void 0 : manager.username) !== ownerFullName
|
|
165
|
-
? `${ownerFullName} (${manager === null || manager === void 0 ? void 0 : manager.username})`
|
|
166
|
-
: manager === null || manager === void 0 ? void 0 : manager.username,
|
|
167
|
-
};
|
|
168
|
-
}),
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
150
|
if (project.data.inContext) {
|
|
172
151
|
defaultSettings.push({
|
|
173
152
|
key: 'inContext',
|
|
@@ -320,30 +299,3 @@ function getOAuthLoginFormId(clientId) {
|
|
|
320
299
|
return `oauth_form_${clientId}`;
|
|
321
300
|
}
|
|
322
301
|
exports.getOAuthLoginFormId = getOAuthLoginFormId;
|
|
323
|
-
function getManagers(client, projectId, ownerId) {
|
|
324
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
-
const managers = [];
|
|
326
|
-
if (client.organization) {
|
|
327
|
-
try {
|
|
328
|
-
const admins = (yield client.usersApi.withFetchAll().listUsers()).data.filter((user) => user.data.isAdmin);
|
|
329
|
-
managers.push(...admins.map((admin) => admin.data));
|
|
330
|
-
}
|
|
331
|
-
catch (e) {
|
|
332
|
-
console.error('Failed to get organization users', e);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
const projectMembers = (yield client.usersApi.withFetchAll().listProjectMembers(projectId)).data.filter((user) => 'role' in user.data ? ['owner', 'manager'].includes(user.data.role) : user.data.isManager);
|
|
336
|
-
managers.push(...projectMembers.map((members) => members.data));
|
|
337
|
-
const uniqueManagers = new Map();
|
|
338
|
-
managers.forEach((manager) => {
|
|
339
|
-
if (manager.id !== ownerId && !uniqueManagers.has(manager.id)) {
|
|
340
|
-
uniqueManagers.set(manager.id, manager);
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
return Array.from(uniqueManagers.values()).sort((a, b) => {
|
|
344
|
-
const aValue = a.firstName || a.username || '';
|
|
345
|
-
const bValue = b.firstName || b.username || '';
|
|
346
|
-
return aValue.localeCompare(bValue);
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
}
|
package/out/modules/manifest.js
CHANGED
|
@@ -275,7 +275,7 @@ function handle(config) {
|
|
|
275
275
|
workflowStep.key = config.identifier + '-' + (0, util_3.getWorkflowStepKey)(workflowStep);
|
|
276
276
|
}
|
|
277
277
|
const uiModule = ((_a = workflowStep === null || workflowStep === void 0 ? void 0 : workflowStep.settingsUiModule) === null || _a === void 0 ? void 0 : _a.formSchema) || ((_b = workflowStep === null || workflowStep === void 0 ? void 0 : workflowStep.settingsUiModule) === null || _b === void 0 ? void 0 : _b.fileName);
|
|
278
|
-
modules['workflow-step-type'].push(Object.assign({ key: workflowStep.key, name: workflowStep.name || config.name, description: workflowStep.description || config.description, boundaries: workflowStep.boundaries, updateSettingsUrl: (0, util_3.getWorkflowStepUrl)('/settings', workflowStep), deleteSettingsUrl: (0, util_3.getWorkflowStepUrl)('/delete', workflowStep) }, (uiModule ? { url: (0, util_3.getWorkflowStepUrl)('/workflow-step', workflowStep) } : {})));
|
|
278
|
+
modules['workflow-step-type'].push(Object.assign(Object.assign(Object.assign({ key: workflowStep.key, name: workflowStep.name || config.name, description: workflowStep.description || config.description, boundaries: workflowStep.boundaries }, (workflowStep.editorMode ? { editorMode: workflowStep.editorMode } : {})), { updateSettingsUrl: (0, util_3.getWorkflowStepUrl)('/settings', workflowStep), deleteSettingsUrl: (0, util_3.getWorkflowStepUrl)('/delete', workflowStep) }), (uiModule ? { url: (0, util_3.getWorkflowStepUrl)('/workflow-step', workflowStep) } : {})));
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
const events = {
|
|
@@ -285,11 +285,14 @@ function handle(config) {
|
|
|
285
285
|
if (!(0, subscription_1.isAppFree)(config)) {
|
|
286
286
|
events['subscription_paid'] = '/subscription-paid';
|
|
287
287
|
}
|
|
288
|
+
const defaultScopes = config.projectIntegration
|
|
289
|
+
? [types_1.Scope.USERS, types_1.Scope.PROJECTS, types_1.Scope.APPLICATIONS]
|
|
290
|
+
: [types_1.Scope.PROJECTS];
|
|
288
291
|
return (_req, res) => {
|
|
289
292
|
const manifest = Object.assign(Object.assign(Object.assign(Object.assign({ identifier: config.identifier, name: config.name, logo: (0, util_1.getLogoUrl)(), baseUrl: config.baseUrl, authentication: {
|
|
290
293
|
type: config.authenticationType || types_1.AuthenticationType.APP,
|
|
291
294
|
clientId: config.clientId,
|
|
292
|
-
} }, (config.agent && { agent: config.agent })), { events, scopes: config.scopes ? config.scopes :
|
|
295
|
+
} }, (config.agent && { agent: config.agent })), { events, scopes: config.scopes ? config.scopes : defaultScopes }), (config.defaultPermissions && { default_permissions: config.defaultPermissions })), { modules });
|
|
293
296
|
res.send(manifest);
|
|
294
297
|
};
|
|
295
298
|
}
|
|
@@ -68,9 +68,7 @@ function webhookHandler(config, webhooks) {
|
|
|
68
68
|
const json = JSON.parse(req.body.toString());
|
|
69
69
|
for (const webhook of webhooks) {
|
|
70
70
|
if (webhook.key === moduleKey) {
|
|
71
|
-
|
|
72
|
-
yield webhook.callback({ credentials, event, client });
|
|
73
|
-
}
|
|
71
|
+
yield webhook.callback({ credentials, events: json.events, client });
|
|
74
72
|
}
|
|
75
73
|
}
|
|
76
74
|
}));
|
|
@@ -26,6 +26,10 @@ export interface WorkflowStepTypeModule extends ModuleKey {
|
|
|
26
26
|
* settings UI module
|
|
27
27
|
*/
|
|
28
28
|
settingsUiModule?: UiModule;
|
|
29
|
+
/**
|
|
30
|
+
* Editor mode that will be used in the editor for this module
|
|
31
|
+
*/
|
|
32
|
+
editorMode?: EditorMode;
|
|
29
33
|
/**
|
|
30
34
|
* input and output boundaries for the module
|
|
31
35
|
*/
|
|
@@ -54,4 +58,9 @@ export interface WorkflowStepTypeModule extends ModuleKey {
|
|
|
54
58
|
client: Crowdin;
|
|
55
59
|
}) => Promise<void>;
|
|
56
60
|
}
|
|
61
|
+
export declare enum EditorMode {
|
|
62
|
+
comfortable = "comfortable",
|
|
63
|
+
sideBySide = "side-by-side",
|
|
64
|
+
multilingual = "multilingual"
|
|
65
|
+
}
|
|
57
66
|
export {};
|
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EditorMode = void 0;
|
|
4
|
+
var EditorMode;
|
|
5
|
+
(function (EditorMode) {
|
|
6
|
+
EditorMode["comfortable"] = "comfortable";
|
|
7
|
+
EditorMode["sideBySide"] = "side-by-side";
|
|
8
|
+
EditorMode["multilingual"] = "multilingual";
|
|
9
|
+
})(EditorMode = exports.EditorMode || (exports.EditorMode = {}));
|