@crowdin/app-project-module 0.7.0 → 0.9.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/README.md CHANGED
@@ -30,6 +30,8 @@ In both options you will need to provide Crowdin App configuration file. Please
30
30
  - [Background tasks](#background-tasks)
31
31
  - [Error propagation](#error-propagation)
32
32
  - [Custom File Format](#custom-file-format)
33
+ - [Custom MT](#custom-mt)
34
+ - [Resources](#resources)
33
35
  - [Contributing](#contributing)
34
36
  - [Seeking Assistance](#seeking-assistance)
35
37
  - [License](#license)
@@ -377,7 +379,7 @@ configuration.integrartion.getIntegrationFiles = async (credentials, appSettings
377
379
 
378
380
  ## Custom File Format
379
381
 
380
- There is also a possibility to declare [custom file format module](https://support.crowdin.com/crowdin-apps-modules/#custom-file-format-module).
382
+ Example of [custom file format module](https://support.crowdin.com/crowdin-apps-modules/#custom-file-format-module).
381
383
 
382
384
  ```javascript
383
385
  const crowdinModule = require('@crowdin/app-project-module');
@@ -425,6 +427,123 @@ const configuration = {
425
427
  crowdinModule.createApp(configuration);
426
428
  ```
427
429
 
430
+ ## Custom MT
431
+
432
+ Example of [custom mt module](https://support.crowdin.com/crowdin-apps-modules/#custom-mt-machine-translation-module).
433
+
434
+ ```javascript
435
+ const crowdinModule = require('@crowdin/app-project-module');
436
+
437
+ const configuration = {
438
+ baseUrl: 'https://123.ngrok.io',
439
+ clientId: 'clientId',
440
+ clientSecret: 'clientSecret',
441
+ name: 'Sample App',
442
+ identifier: 'sample-app',
443
+ description: 'Sample App description',
444
+ dbFolder: __dirname,
445
+ imagePath: __dirname + '/' + 'logo.png',
446
+ customMT: {
447
+ translate: async (client, context, projectId, source, target, strings) => {
448
+ //translate strings
449
+ const translations = ['hello', 'world'];
450
+ return translations;
451
+ }
452
+ }
453
+ };
454
+
455
+ crowdinModule.createApp(configuration);
456
+ ```
457
+
458
+ ## Resources
459
+
460
+ Example of [resources module](https://support.crowdin.com/crowdin-apps-modules/#resources-module).
461
+
462
+ ```javascript
463
+ const crowdinModule = require('@crowdin/app-project-module');
464
+
465
+ const configuration = {
466
+ baseUrl: 'https://123.ngrok.io',
467
+ clientId: 'clientId',
468
+ clientSecret: 'clientSecret',
469
+ name: 'Sample App',
470
+ identifier: 'sample-app',
471
+ description: 'Sample App description',
472
+ dbFolder: __dirname,
473
+ imagePath: __dirname + '/' + 'logo.png',
474
+ resources: {
475
+ fileName: 'setup.html',
476
+ uiPath: __dirname + '/' + 'public'
477
+ },
478
+ };
479
+
480
+ crowdinModule.createApp(configuration);
481
+ ```
482
+
483
+ The resources module can work as an extension to other modules be something like a configuration UI for them.
484
+
485
+ ```javascript
486
+ const crowdinModule = require('@crowdin/app-project-module');
487
+ const express = require('express');
488
+
489
+ const app = express();
490
+
491
+ const configuration = {
492
+ baseUrl: 'https://123.ngrok.io',
493
+ clientId: 'clientId',
494
+ clientSecret: 'clientSecret',
495
+ name: 'Sample App',
496
+ identifier: 'sample-app',
497
+ description: 'Sample App description',
498
+ dbFolder: __dirname,
499
+ imagePath: __dirname + '/' + 'logo.png',
500
+ resources: {
501
+ fileName: 'setup.html',
502
+ uiPath: __dirname + '/' + 'public'
503
+ },
504
+ customMT: {
505
+ translate,
506
+ },
507
+ onUninstall: cleanup
508
+ };
509
+
510
+ const crowdinApp = crowdinModule.addCrowdinEndpoints(app, configuration);
511
+
512
+ async function cleanup(organization) {
513
+ await crowdinApp.deleteMetadata(organization);
514
+ }
515
+
516
+ async function translate(crowdinClient, context, projectId, source, target, strings) {
517
+ const organization = context.jwtPayload.domain || context.jwtPayload.context.organization_id;
518
+ const metadata = await crowdinApp.getMetadata(organization);
519
+ //do translation based on metadata
520
+ const translations = ['hello', 'world'];
521
+ return translations;
522
+ }
523
+
524
+ //extra endpoints for resources UI
525
+ app.post('/metadata', async (req, res) => {
526
+ const { context } = await crowdinApp.establishCrowdinConnection(req.query.jwt);
527
+ const organization = context.jwtPayload.domain || context.jwtPayload.context.organization_id;
528
+ const metadata = await crowdinApp.getMetadata(organization);
529
+ if (metadata) {
530
+ await crowdinApp.updateMetadata(organization, req.body);
531
+ } else {
532
+ await crowdinApp.saveMetadata(organization, req.body);
533
+ }
534
+ res.status(204).end();
535
+ });
536
+
537
+ app.get('/metadata', async (req, res) => {
538
+ const { context } = await crowdinApp.establishCrowdinConnection(req.query.jwt);
539
+ const organization = context.jwtPayload.domain || context.jwtPayload.context.organization_id;
540
+ const metadata = await crowdinApp.getMetadata(organization) || {};
541
+ res.status(200).send(metadata);
542
+ });
543
+
544
+ app.listen(3000, () => console.log('Crowdin app started'));
545
+ ```
546
+
428
547
  ## Contributing
429
548
 
430
549
  If you want to contribute please read the [Contributing](/CONTRIBUTING.md) guidelines.
@@ -0,0 +1,4 @@
1
+ /// <reference types="qs" />
2
+ import { Response } from 'express';
3
+ import { CustomMTLogic } from '../../models';
4
+ export default function handle(config: CustomMTLogic): (req: 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,31 @@
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
+ function handle(config) {
14
+ return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
15
+ const source = req.query.source;
16
+ const target = req.query.target;
17
+ const body = req.body;
18
+ const projectId = Number(req.query.project_id);
19
+ if (source === 'en' && target === 'de' && body.strings && body.strings[0] === 'validation') {
20
+ if (config.validate) {
21
+ yield config.validate(req.crowdinApiClient);
22
+ }
23
+ res.send({ data: { translations: [] } });
24
+ }
25
+ else {
26
+ const translations = yield config.translate(req.crowdinApiClient, req.crowdinContext, projectId, source, target, body.strings);
27
+ res.send({ data: { translations } });
28
+ }
29
+ }));
30
+ }
31
+ exports.default = handle;
@@ -5,7 +5,7 @@ function handle(config) {
5
5
  if (config.integration) {
6
6
  modules.integrations = [
7
7
  {
8
- key: config.identifier,
8
+ key: config.identifier + '-int',
9
9
  name: config.name,
10
10
  description: config.description,
11
11
  logo: '/logo.png',
@@ -16,13 +16,40 @@ function handle(config) {
16
16
  if (config.customFileFormat) {
17
17
  modules['custom-file-format'] = [
18
18
  {
19
- key: config.identifier,
19
+ key: config.identifier + '-ff',
20
20
  type: config.customFileFormat.type,
21
21
  signaturePatterns: config.customFileFormat.signaturePatterns,
22
22
  url: '/process',
23
23
  },
24
24
  ];
25
25
  }
26
+ if (config.customMT) {
27
+ modules['custom-mt'] = [
28
+ {
29
+ key: config.identifier + '-mt',
30
+ name: config.name,
31
+ url: '/translate',
32
+ },
33
+ ];
34
+ }
35
+ if (config.resources) {
36
+ modules['organization-menu'] = [
37
+ {
38
+ key: config.identifier + '-resources',
39
+ name: config.name,
40
+ url: '/resources/' + (config.resources.fileName || 'index.html'),
41
+ icon: '/logo.png',
42
+ },
43
+ ];
44
+ modules['resources'] = [
45
+ {
46
+ key: config.identifier + '-resources',
47
+ name: config.name,
48
+ url: '/resources/' + (config.resources.fileName || 'index.html'),
49
+ icon: '/logo.png',
50
+ },
51
+ ];
52
+ }
26
53
  return (_req, res) => {
27
54
  const manifest = {
28
55
  identifier: config.identifier,
@@ -1,3 +1,4 @@
1
1
  /// <reference types="qs" />
2
2
  import { Request, Response } from 'express';
3
- export default function handle(): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
3
+ import { Config } from '../models';
4
+ export default function handle(config: Config): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
@@ -11,10 +11,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  const storage_1 = require("../storage");
13
13
  const util_1 = require("../util");
14
- function handle() {
14
+ function handle(config) {
15
15
  return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
16
16
  const event = req.body;
17
- yield (0, storage_1.deleteCrowdinCredentials)((event.domain || event.organizationId).toString());
17
+ const organization = (event.domain || event.organizationId).toString();
18
+ yield (0, storage_1.deleteCrowdinCredentials)(organization);
19
+ if (config.onUninstall) {
20
+ yield config.onUninstall(organization);
21
+ }
18
22
  res.status(204).end();
19
23
  }));
20
24
  }
package/out/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Express } from 'express';
2
- import { Config } from './models';
3
- export declare function addCrowdinEndpoints(app: Express, config: Config): void;
2
+ import { Config, CrowdinAppUtilities } from './models';
3
+ export declare function addCrowdinEndpoints(app: Express, config: Config): CrowdinAppUtilities;
4
4
  export declare function createApp(config: Config): void;
package/out/index.js CHANGED
@@ -33,6 +33,7 @@ const crowdin_project_1 = __importDefault(require("./handlers/crowdin-project"))
33
33
  const crowdin_update_1 = __importDefault(require("./handlers/crowdin-update"));
34
34
  const download_1 = __importDefault(require("./handlers/custom-file-format/download"));
35
35
  const process_1 = __importDefault(require("./handlers/custom-file-format/process"));
36
+ const translate_1 = __importDefault(require("./handlers/custom-mt/translate"));
36
37
  const install_1 = __importDefault(require("./handlers/install"));
37
38
  const integration_data_1 = __importDefault(require("./handlers/integration-data"));
38
39
  const integration_login_1 = __importDefault(require("./handlers/integration-login"));
@@ -43,13 +44,13 @@ const manifest_1 = __importDefault(require("./handlers/manifest"));
43
44
  const oauth_login_1 = __importDefault(require("./handlers/oauth-login"));
44
45
  const settings_save_1 = __importDefault(require("./handlers/settings-save"));
45
46
  const uninstall_1 = __importDefault(require("./handlers/uninstall"));
46
- const crowdin_client_1 = __importDefault(require("./middlewares/crowdin-client"));
47
+ const crowdin_client_1 = __importStar(require("./middlewares/crowdin-client"));
47
48
  const integration_credentials_1 = __importDefault(require("./middlewares/integration-credentials"));
48
49
  const json_response_1 = __importDefault(require("./middlewares/json-response"));
49
- const storage_1 = require("./storage");
50
+ const storage = __importStar(require("./storage"));
50
51
  const util_1 = require("./util");
51
52
  function addCrowdinEndpoints(app, config) {
52
- (0, storage_1.connect)(config.dbFolder);
53
+ storage.connect(config.dbFolder);
53
54
  app.use(express_1.default.json({ limit: '50mb' }));
54
55
  app.use('/assets', express_1.default.static((0, path_1.join)(__dirname, 'static')));
55
56
  app.set('views', (0, path_1.join)(__dirname, 'views'));
@@ -68,7 +69,7 @@ function addCrowdinEndpoints(app, config) {
68
69
  app.set('view engine', 'handlebars');
69
70
  app.get('/logo.png', (req, res) => res.sendFile(config.imagePath || (0, path_1.join)(__dirname, 'logo.png')));
70
71
  app.post('/installed', (0, install_1.default)(config));
71
- app.post('/uninstall', (0, uninstall_1.default)());
72
+ app.post('/uninstall', (0, uninstall_1.default)(config));
72
73
  app.get('/manifest.json', json_response_1.default, (0, manifest_1.default)(config));
73
74
  const integrationLogic = config.integration;
74
75
  if (integrationLogic) {
@@ -96,6 +97,19 @@ function addCrowdinEndpoints(app, config) {
96
97
  app.post('/process', (0, process_1.default)(config.baseUrl, config.customFileFormat.filesFolder || config.dbFolder, config.customFileFormat));
97
98
  app.get('/file/download', (0, download_1.default)(config.customFileFormat.filesFolder || config.dbFolder));
98
99
  }
100
+ if (config.customMT) {
101
+ app.post('/translate', (0, crowdin_client_1.default)(config), (0, translate_1.default)(config.customMT));
102
+ }
103
+ if (config.resources) {
104
+ app.use('/resources', express_1.default.static(config.resources.uiPath));
105
+ }
106
+ return {
107
+ getMetadata: storage.getMetadata,
108
+ saveMetadata: storage.saveMetadata,
109
+ updateMetadata: storage.updateMetadata,
110
+ deleteMetadata: storage.deleteMetadata,
111
+ establishCrowdinConnection: jwtToken => (0, crowdin_client_1.prepareCrowdinRequest)(jwtToken, config),
112
+ };
99
113
  }
100
114
  exports.addCrowdinEndpoints = addCrowdinEndpoints;
101
115
  function createApp(config) {
@@ -1,4 +1,9 @@
1
1
  /// <reference types="qs" />
2
+ import Crowdin from '@crowdin/crowdin-api-client';
2
3
  import { Response } from 'express';
3
- import { Config } from '../models';
4
+ import { Config, CrowdinContextInfo } from '../models';
5
+ export declare function prepareCrowdinRequest(jwtToken: string, config: Config, optional?: boolean): Promise<{
6
+ context: CrowdinContextInfo;
7
+ client?: Crowdin;
8
+ }>;
4
9
  export default function handle(config: Config, optional?: boolean): (req: 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;
@@ -9,42 +9,60 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.prepareCrowdinRequest = void 0;
12
13
  const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
13
14
  const storage_1 = require("../storage");
14
15
  const util_1 = require("../util");
15
- function handle(config, optional = false) {
16
- return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
17
- const origin = req.query.origin;
18
- const clientId = req.query.client_id;
19
- const tokenJwt = req.query.tokenJwt;
20
- if (!origin || !clientId || !tokenJwt || clientId !== config.clientId) {
21
- return res.status(403).send({ error: 'Access denied' });
22
- }
16
+ function prepareCrowdinRequest(jwtToken, config, optional = false) {
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ let context;
19
+ let client;
23
20
  try {
24
- const jwtPayload = yield (0, crowdin_apps_functions_1.validateJwtToken)(tokenJwt, config.clientSecret);
25
- req.crowdinContext = {
21
+ const jwtPayload = yield (0, crowdin_apps_functions_1.validateJwtToken)(jwtToken, config.clientSecret);
22
+ context = {
26
23
  jwtPayload,
27
24
  clientId: (0, crowdin_apps_functions_1.constructCrowdinIdFromJwtPayload)(jwtPayload),
28
25
  crowdinId: `${jwtPayload.domain || jwtPayload.context.organization_id}`,
29
26
  };
30
27
  }
31
28
  catch (e) {
32
- return res.status(403).send({ error: "Can't verify" });
29
+ throw new Error("Can't verify");
33
30
  }
34
31
  try {
35
- const credentials = yield (0, storage_1.getCrowdinCredentials)(req.crowdinContext.crowdinId);
32
+ const credentials = yield (0, storage_1.getCrowdinCredentials)(context.crowdinId);
36
33
  if (!credentials) {
37
34
  if (optional) {
38
- return next();
35
+ return { context };
39
36
  }
40
- return res.status(403).send({ error: "Can't find organization by id" });
37
+ throw new Error("Can't find organization by id");
41
38
  }
42
- req.crowdinApiClient = yield (0, util_1.prepareCrowdinClient)(config, credentials);
43
- next();
39
+ client = yield (0, util_1.prepareCrowdinClient)(config, credentials);
44
40
  }
45
41
  catch (e) {
42
+ throw new Error('Access denied');
43
+ }
44
+ return { context, client };
45
+ });
46
+ }
47
+ exports.prepareCrowdinRequest = prepareCrowdinRequest;
48
+ function handle(config, optional = false) {
49
+ return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
50
+ const tokenJwt = (req.query.tokenJwt || req.query.jwtToken);
51
+ if (!tokenJwt) {
46
52
  return res.status(403).send({ error: 'Access denied' });
47
53
  }
54
+ try {
55
+ const data = yield prepareCrowdinRequest(tokenJwt, config, optional);
56
+ req.crowdinContext = data.context;
57
+ if (data.client) {
58
+ req.crowdinApiClient = data.client;
59
+ }
60
+ next();
61
+ }
62
+ catch (e) {
63
+ const message = typeof e === 'string' ? e : e.message;
64
+ return res.status(403).send({ error: message || 'Error' });
65
+ }
48
66
  }));
49
67
  }
50
68
  exports.default = handle;
@@ -46,6 +46,18 @@ export interface Config {
46
46
  * custom file format module logic
47
47
  */
48
48
  customFileFormat?: CustomFileFormatLogic;
49
+ /**
50
+ * custom MT module logic
51
+ */
52
+ customMT?: CustomMTLogic;
53
+ /**
54
+ * resources module
55
+ */
56
+ resources?: Resources;
57
+ /**
58
+ * Uninstall hook for cleanup logic
59
+ */
60
+ onUninstall?: (organization: string) => Promise<void>;
49
61
  }
50
62
  export interface IntegrationLogic {
51
63
  /**
@@ -345,3 +357,30 @@ export interface ProcessFileString {
345
357
  text: string | SourceStringsModel.PluralText;
346
358
  translations?: any;
347
359
  }
360
+ export interface CustomMTLogic {
361
+ translate: (client: Crowdin, context: CrowdinContextInfo, projectId: number, source: string, target: string, strings: string[]) => Promise<string[]>;
362
+ validate?: (client: Crowdin) => Promise<void>;
363
+ }
364
+ export interface CustomMTRequest {
365
+ strings: string[];
366
+ }
367
+ export interface Resources {
368
+ /**
369
+ * path to ui folder (e.g. {@example join(__dirname, 'public')})
370
+ */
371
+ uiPath: string;
372
+ /**
373
+ * page name (default index.html)
374
+ */
375
+ fileName?: string;
376
+ }
377
+ export interface CrowdinAppUtilities {
378
+ saveMetadata: (id: string, metadata: any) => Promise<void>;
379
+ updateMetadata: (id: string, metadata: any) => Promise<void>;
380
+ getMetadata: (id: string) => Promise<any | undefined>;
381
+ deleteMetadata: (id: string) => Promise<void>;
382
+ establishCrowdinConnection: (jwtToken: string) => Promise<{
383
+ context: CrowdinContextInfo;
384
+ client?: Crowdin;
385
+ }>;
386
+ }
@@ -11,3 +11,7 @@ export declare function updateIntegrationConfig(id: string, config: any): Promis
11
11
  export declare function getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
12
12
  export declare function getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
13
13
  export declare function deleteIntegrationCredentials(id: string): Promise<void>;
14
+ export declare function saveMetadata(id: string, metadata: any): Promise<void>;
15
+ export declare function updateMetadata(id: string, metadata: any): Promise<void>;
16
+ export declare function getMetadata(id: string): Promise<any | undefined>;
17
+ export declare function deleteMetadata(id: string): Promise<void>;
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.deleteIntegrationCredentials = exports.getAllIntegrationCredentials = exports.getIntegrationCredentials = exports.updateIntegrationConfig = exports.updateIntegrationCredentials = exports.saveIntegrationCredentials = exports.deleteCrowdinCredentials = exports.getAllCrowdinCredentials = exports.getCrowdinCredentials = exports.updateCrowdinCredentials = exports.saveCrowdinCredentials = exports.connect = void 0;
16
+ exports.deleteMetadata = exports.getMetadata = exports.updateMetadata = exports.saveMetadata = exports.deleteIntegrationCredentials = exports.getAllIntegrationCredentials = exports.getIntegrationCredentials = exports.updateIntegrationConfig = exports.updateIntegrationCredentials = exports.saveIntegrationCredentials = exports.deleteCrowdinCredentials = exports.getAllCrowdinCredentials = exports.getCrowdinCredentials = exports.updateCrowdinCredentials = exports.saveCrowdinCredentials = exports.connect = void 0;
17
17
  const path_1 = require("path");
18
18
  const sqlite3_1 = __importDefault(require("sqlite3"));
19
19
  let _res;
@@ -74,6 +74,13 @@ function connect(folder) {
74
74
  crowdin_id varchar not null
75
75
  );
76
76
  `, []);
77
+ yield _run(`
78
+ create table if not exists app_metadata
79
+ (
80
+ id varchar not null primary key,
81
+ data varchar null
82
+ );
83
+ `, []);
77
84
  _res();
78
85
  }
79
86
  catch (e) {
@@ -198,3 +205,26 @@ function deleteIntegrationCredentials(id) {
198
205
  return run('DELETE FROM integration_credentials where id = ?', [id]);
199
206
  }
200
207
  exports.deleteIntegrationCredentials = deleteIntegrationCredentials;
208
+ function saveMetadata(id, metadata) {
209
+ return run('INSERT INTO app_metadata(id, data) VALUES (?, ?)', [id, JSON.stringify(metadata)]);
210
+ }
211
+ exports.saveMetadata = saveMetadata;
212
+ function updateMetadata(id, metadata) {
213
+ return run('UPDATE app_metadata SET data = ? WHERE id = ?', [JSON.stringify(metadata), id]);
214
+ }
215
+ exports.updateMetadata = updateMetadata;
216
+ function getMetadata(id) {
217
+ return __awaiter(this, void 0, void 0, function* () {
218
+ const row = yield get('SELECT data FROM app_metadata WHERE id = ?', [id]);
219
+ if (row) {
220
+ return JSON.parse(row.data);
221
+ }
222
+ });
223
+ }
224
+ exports.getMetadata = getMetadata;
225
+ function deleteMetadata(id) {
226
+ return __awaiter(this, void 0, void 0, function* () {
227
+ yield run('DELETE FROM app_metadata where id = ?', [id]);
228
+ });
229
+ }
230
+ exports.deleteMetadata = deleteMetadata;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
5
5
  "main": "out/index.js",
6
6
  "types": "out/index.d.ts",