@emilgroup/translation-sdk 1.1.0 → 1.1.1-beta.11

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/api.ts CHANGED
@@ -15,17 +15,15 @@
15
15
 
16
16
  import { Configuration } from './configuration';
17
17
  import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
18
- // URLSearchParams not necessarily used
19
- // @ts-ignore
20
- import { URL, URLSearchParams } from 'url';
21
- import FormData from 'form-data'
22
18
  // Some imports not used depending on template conditions
23
19
  // @ts-ignore
24
20
  import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from './common';
25
21
  // @ts-ignore
26
22
  import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from './base';
23
+ import { DefaultApi } from './api';
27
24
  import { TranslationApi } from './api';
28
25
 
29
26
 
27
+ export * from './api/default-api';
30
28
  export * from './api/translation-api';
31
29
 
package/base.ts CHANGED
@@ -14,20 +14,13 @@
14
14
 
15
15
 
16
16
  import { Configuration } from "./configuration";
17
+ import { defaultStorage } from "./common";
17
18
  // Some imports not used depending on template conditions
18
19
  // @ts-ignore
19
20
  import globalAxios, { AxiosPromise, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
20
- import * as fs from 'fs';
21
- import * as path from 'path';
22
- import * as os from 'os';
23
21
 
24
- export const BASE_PATH = "https://apiv2.emil.de".replace(/\/+$/, "");
25
- const CONFIG_DIRECTORY = '.emil';
26
- const CONFIG_FILENAME = 'credentials';
27
- const KEY_USERNAME = 'emil_username';
28
- const KEY_PASSWORD = 'emil_password';
29
22
 
30
- const filePath = os.homedir() + path.sep + CONFIG_DIRECTORY + path.sep + CONFIG_FILENAME;
23
+ export const BASE_PATH = "https://apiv2.emil.de".replace(/\/+$/, "");
31
24
  /**
32
25
  *
33
26
  * @export
@@ -41,13 +34,25 @@ export const COLLECTION_FORMATS = {
41
34
 
42
35
  export interface LoginClass {
43
36
  accessToken: string;
44
- permissions: Array<string>;
37
+ permissions: string;
38
+ }
39
+
40
+ export interface SwitchWorkspaceRequest {
41
+ username: string;
42
+ targetWorkspace: string;
43
+ }
44
+
45
+ export interface SwitchWorkspaceResponseClass {
46
+ accessToken: string;
47
+ permissions: string;
45
48
  }
46
49
 
47
50
  export enum Environment {
48
51
  Production = 'https://apiv2.emil.de',
49
52
  Test = 'https://apiv2-test.emil.de',
53
+ Staging = 'https://apiv2-staging.emil.de',
50
54
  Development = 'https://apiv2-dev.emil.de',
55
+ ProductionZurich = 'https://eu-central-2.apiv2.emil.de',
51
56
  }
52
57
 
53
58
  let _retry_count = 0
@@ -67,7 +72,14 @@ export interface RequestArgs {
67
72
  options: AxiosRequestConfig;
68
73
  }
69
74
 
75
+ interface TokenData {
76
+ accessToken?: string;
77
+ username?: string;
78
+ permissions?: string;
79
+ }
80
+
70
81
  const NETWORK_ERROR_MESSAGE = "Network Error";
82
+ const TOKEN_DATA = 'APP_TOKEN';
71
83
 
72
84
  /**
73
85
  *
@@ -75,80 +87,54 @@ const NETWORK_ERROR_MESSAGE = "Network Error";
75
87
  * @class BaseAPI
76
88
  */
77
89
  export class BaseAPI {
78
- protected configuration: Configuration;
79
- private username?: string;
80
- private password?: string;
90
+ protected configuration: Configuration | undefined;
91
+ private tokenData?: TokenData;
92
+
93
+ constructor(configuration?: Configuration,
94
+ protected basePath: string = BASE_PATH,
95
+ protected axios: AxiosInstance = globalAxios) {
96
+
97
+ this.loadTokenData();
81
98
 
82
- constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) {
83
99
  if (configuration) {
100
+ const { accessToken } = this.tokenData;
84
101
  this.configuration = configuration;
85
102
  this.basePath = configuration.basePath || this.basePath;
103
+
104
+ // Use config token if provided, otherwise use tokenData token
105
+ const configToken = this.configuration.accessToken;
106
+ const storedToken = accessToken ? `Bearer ${accessToken}` : '';
107
+ this.configuration.accessToken = configToken || storedToken;
86
108
  } else {
109
+ const { accessToken, username } = this.tokenData;
110
+
87
111
  this.configuration = new Configuration({
88
112
  basePath: this.basePath,
113
+ accessToken: accessToken ? `Bearer ${accessToken}` : '',
114
+ username,
89
115
  });
90
116
  }
91
117
 
92
118
  this.attachInterceptor(axios);
93
119
  }
94
120
 
95
- async initialize(env: Environment = Environment.Production) {
96
- this.configuration.basePath = env;
97
-
98
- await this.loadCredentials();
99
-
100
- if (this.username) {
101
- await this.authorize(this.username, this.password);
102
- this.password = null; // to avoid keeping password loaded in memory.
103
- }
104
- }
105
-
106
- private async loadCredentials() {
107
- try {
108
- await this.readConfigFile();
109
- } catch (error) {
110
- console.warn(`No credentials file found. Check that ${filePath} exists.`);
111
- }
112
-
113
- this.readEnvVariables();
114
-
115
- if (!this.username) {
116
- console.info(`No credentials found in credentials file or environment variables. Either provide some or use
117
- authorize() function.`);
118
- }
121
+ selectEnvironment(env: Environment) {
122
+ this.selectBasePath(env);
119
123
  }
120
124
 
121
- private async readConfigFile() {
122
- const file = await fs.promises.readFile(filePath, 'utf-8');
123
-
124
- const lines = file.split(os.EOL)
125
- .filter(Boolean);
126
-
127
- lines.forEach((line: string) => {
128
- if (line.startsWith(KEY_USERNAME)) {
129
- this.username = line.length > KEY_USERNAME.length + 1 ? line.substring(KEY_USERNAME.length + 1) : '';
130
- } else if (line.startsWith(KEY_PASSWORD)) {
131
- this.password = line.length > KEY_PASSWORD.length + 1 ? line.substring(KEY_PASSWORD.length + 1) : '';
132
- }
133
- });
125
+ selectBasePath(path: string) {
126
+ this.configuration.basePath = path;
134
127
  }
135
128
 
136
- private readEnvVariables(): boolean {
137
- if (process.env.EMIL_USERNAME) {
138
- this.username = process.env.EMIL_USERNAME;
139
- this.password = process.env.EMIL_PASSWORD || '';
140
-
141
- return true;
129
+ getPermissions(): Array<string> {
130
+ if (!this.tokenData?.permissions) {
131
+ return [];
142
132
  }
143
133
 
144
- return false;
145
- }
146
-
147
- selectEnvironment(env: Environment) {
148
- this.configuration.basePath = env;
134
+ return this.tokenData.permissions.split(',');
149
135
  }
150
136
 
151
- async authorize(username: string, password: string): Promise<void> {
137
+ async authorize(username: string, password: string, targetWorkspace?: string): Promise<void> {
152
138
  const options: AxiosRequestConfig = {
153
139
  method: 'POST',
154
140
  url: `${this.configuration.basePath}/authservice/v1/login`,
@@ -162,50 +148,104 @@ export class BaseAPI {
162
148
 
163
149
  const response = await globalAxios.request<LoginClass>(options);
164
150
 
165
- const { data: { accessToken } } = response;
151
+ const { data: { accessToken, permissions } } = response;
152
+
166
153
  this.configuration.username = username;
167
154
  this.configuration.accessToken = `Bearer ${accessToken}`;
155
+ this.tokenData.username = username;
156
+ this.tokenData.accessToken = accessToken;
157
+ this.tokenData.permissions = permissions;
168
158
 
169
- const refreshToken = this.extractRefreshToken(response)
170
- this.configuration.refreshToken = refreshToken;
159
+ // Switch workspace if provided
160
+ if (targetWorkspace) {
161
+ await this.switchWorkspace(targetWorkspace);
162
+ } else {
163
+ // Only store if no workspace switch (since switchWorkspace will store after switching)
164
+ this.storeTokenData({
165
+ ...this.tokenData
166
+ });
167
+ }
168
+
169
+ this.storeTokenData({
170
+ ...this.tokenData
171
+ });
171
172
  }
172
173
 
173
- async refreshTokenInternal(): Promise<string> {
174
- const { username, refreshToken } = this.configuration;
174
+ async switchWorkspace(targetWorkspace: string): Promise<void> {
175
+ const options: AxiosRequestConfig = {
176
+ method: 'POST',
177
+ url: `${this.configuration.basePath}/authservice/v1/workspaces/switch`,
178
+ headers: {
179
+ 'Content-Type': 'application/json',
180
+ 'Authorization': `Bearer ${this.configuration.accessToken}`,
181
+ },
182
+ data: {
183
+ username: this.configuration.username,
184
+ targetWorkspace,
185
+ } as SwitchWorkspaceRequest,
186
+ withCredentials: true,
187
+ };
188
+
189
+ const response = await globalAxios.request<SwitchWorkspaceResponseClass>(options);
190
+
191
+ const { data: { accessToken, permissions } } = response;
175
192
 
193
+ this.configuration.accessToken = `Bearer ${accessToken}`;
194
+ this.tokenData.accessToken = accessToken;
195
+ this.tokenData.permissions = permissions;
196
+
197
+ this.storeTokenData({
198
+ ...this.tokenData
199
+ });
200
+ }
176
201
 
177
- if (!username || !refreshToken) {
178
- return '';
202
+ async refreshTokenInternal(): Promise<LoginClass> {
203
+ const { username } = this.configuration;
204
+
205
+ if (!username) {
206
+ throw new Error('Failed to refresh token.');
179
207
  }
180
208
 
209
+ const refreshTokenAxios = globalAxios.create()
210
+ refreshTokenAxios.interceptors.response.use(response => {
211
+ const { permissions } = response.data;
212
+
213
+ this.tokenData.permissions = permissions;
214
+ this.storeTokenData(this.tokenData);
215
+
216
+ return response;
217
+ })
218
+
181
219
  const options: AxiosRequestConfig = {
182
220
  method: 'POST',
183
221
  url: `${this.configuration.basePath}/authservice/v1/refresh-token`,
184
222
  headers: {
185
223
  'Content-Type': 'application/json',
186
- Cookie: refreshToken,
187
224
  },
188
225
  data: { username: username },
189
226
  withCredentials: true,
190
227
  };
191
228
 
192
- const { data: { accessToken } } = await globalAxios.request<LoginClass>(options);
193
-
194
- return accessToken;
229
+ const response = await refreshTokenAxios.request<LoginClass>(options);
230
+ return response.data;
195
231
  }
196
232
 
197
- private extractRefreshToken(response: AxiosResponse): string {
198
- if (response.headers && response.headers['set-cookie']
199
- && response.headers['set-cookie'].length > 0) {
200
-
201
- return `${response.headers['set-cookie'][0].split(';')[0]};`;
233
+ private storeTokenData(tokenData?: TokenData) {
234
+ if (typeof window !== 'undefined') {
235
+ defaultStorage().set<TokenData>(TOKEN_DATA, tokenData);
202
236
  }
237
+ }
203
238
 
204
- return '';
239
+ public loadTokenData() {
240
+ if (typeof window !== 'undefined') {
241
+ this.tokenData = defaultStorage().get<TokenData>(TOKEN_DATA) || {};
242
+ } else {
243
+ this.tokenData = {};
244
+ }
205
245
  }
206
246
 
207
- getConfiguration(): Configuration {
208
- return this.configuration;
247
+ public cleanTokenData() {
248
+ this.storeTokenData(null);
209
249
  }
210
250
 
211
251
  private attachInterceptor(axios: AxiosInstance) {
@@ -215,19 +255,26 @@ export class BaseAPI {
215
255
  },
216
256
  async (err) => {
217
257
  let originalConfig = err.config;
218
- if (err.response) {
258
+ if (err.response && !(err.response instanceof XMLHttpRequest)) { // sometimes buggy and is of type request
219
259
  // Access Token was expired
220
- if (err.response.status === 401 && !originalConfig._retry) {
260
+ if ((err.response.status === 401 || err.response.status === 403)
261
+ && !originalConfig._retry) {
221
262
  originalConfig._retry = true;
222
263
  try {
223
- const tokenString = await this.refreshTokenInternal();
264
+ const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
224
265
  const accessToken = `Bearer ${tokenString}`;
266
+ this.tokenData.permissions = permissions;
267
+
268
+ delete originalConfig.headers['Authorization']
225
269
 
226
- originalConfig.headers['Authorization'] = `Bearer ${accessToken}`
270
+ originalConfig.headers['Authorization'] = accessToken;
227
271
 
228
272
  this.configuration.accessToken = accessToken;
273
+ this.tokenData.accessToken = tokenString;
229
274
 
230
- return axios.request(originalConfig);
275
+ this.storeTokenData(this.tokenData);
276
+
277
+ return axios(originalConfig);
231
278
  } catch (_error) {
232
279
  if (_error.response && _error.response.data) {
233
280
  return Promise.reject(_error.response.data);
@@ -235,23 +282,23 @@ export class BaseAPI {
235
282
  return Promise.reject(_error);
236
283
  }
237
284
  }
238
- if (err.response.status === 403 && err.response.data) {
239
- return Promise.reject(err.response.data);
240
- }
241
- } else if(err.message === NETWORK_ERROR_MESSAGE
242
- && err.isAxiosError
285
+ } else if (err.message === NETWORK_ERROR_MESSAGE
243
286
  && originalConfig.headers.hasOwnProperty('Authorization')
244
287
  && _retry_count < 4
245
- ){
288
+ ) {
246
289
  _retry_count++;
247
290
  try {
248
- const tokenString = await this.refreshTokenInternal();
291
+ const { accessToken: tokenString, permissions } = await this.refreshTokenInternal();
249
292
  const accessToken = `Bearer ${tokenString}`;
293
+ this.tokenData.permissions = permissions;
250
294
 
251
295
  _retry = true;
252
- originalConfig.headers['Authorization'] = accessToken;
296
+ originalConfig.headers['Authorization'] = accessToken;
253
297
 
254
298
  this.configuration.accessToken = accessToken;
299
+ this.tokenData.accessToken = tokenString;
300
+
301
+ this.storeTokenData(this.tokenData);
255
302
 
256
303
  return axios.request({
257
304
  ...originalConfig,
@@ -261,7 +308,7 @@ export class BaseAPI {
261
308
  return Promise.reject(_error.response.data);
262
309
  }
263
310
  return Promise.reject(_error);
264
- }
311
+ }
265
312
  }
266
313
  return Promise.reject(err);
267
314
  }
@@ -276,7 +323,7 @@ export class BaseAPI {
276
323
  * @extends {Error}
277
324
  */
278
325
  export class RequiredError extends Error {
279
- name: "RequiredError" = "RequiredError";
326
+ override name: "RequiredError" = "RequiredError";
280
327
  constructor(public field: string, msg?: string) {
281
328
  super(msg);
282
329
  }
package/common.ts CHANGED
@@ -16,7 +16,6 @@
16
16
  import { Configuration } from "./configuration";
17
17
  import { RequiredError, RequestArgs } from "./base";
18
18
  import { AxiosInstance, AxiosResponse } from 'axios';
19
- import { URL, URLSearchParams } from 'url';
20
19
  /**
21
20
  *
22
21
  * @export
@@ -66,7 +65,7 @@ export const setBearerAuthToObject = async function (object: any, configuration?
66
65
  const accessToken = typeof configuration.accessToken === 'function'
67
66
  ? await configuration.accessToken()
68
67
  : await configuration.accessToken;
69
- object["Authorization"] = "Bearer " + accessToken;
68
+ object["Authorization"] = configuration.getBearerToken(accessToken);
70
69
  }
71
70
  }
72
71
 
@@ -79,7 +78,7 @@ export const setOAuthToObject = async function (object: any, name: string, scope
79
78
  const localVarAccessTokenValue = typeof configuration.accessToken === 'function'
80
79
  ? await configuration.accessToken(name, scopes)
81
80
  : await configuration.accessToken;
82
- object["Authorization"] = "Bearer " + localVarAccessTokenValue;
81
+ object["Authorization"] = configuration.getBearerToken(localVarAccessTokenValue);
83
82
  }
84
83
  }
85
84
 
package/configuration.ts CHANGED
@@ -74,14 +74,6 @@ export class Configuration {
74
74
  */
75
75
  formDataCtor?: new () => any;
76
76
 
77
- /**
78
- * parameter for automatically refreshing access token for oauth2 security
79
- *
80
- * @type {string}
81
- * @memberof Configuration
82
- */
83
- refreshToken?: string;
84
-
85
77
  constructor(param: ConfigurationParameters = {}) {
86
78
  this.apiKey = param.apiKey;
87
79
  this.username = param.username;
@@ -106,4 +98,13 @@ export class Configuration {
106
98
  const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i');
107
99
  return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json');
108
100
  }
101
+
102
+ /**
103
+ * Returns "Bearer" token.
104
+ * @param token - access token.
105
+ * @return Bearer token.
106
+ */
107
+ public getBearerToken(token?: string): string {
108
+ return ('' + token).startsWith("Bearer") ? token : "Bearer " + token;
109
+ }
109
110
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * EMIL TranslationService
3
+ * The EMIL TranslationService API description
4
+ *
5
+ * The version of the OpenAPI document: 1.0
6
+ * Contact: kontakt@emil.de
7
+ *
8
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
9
+ * https://openapi-generator.tech
10
+ * Do not edit the class manually.
11
+ */
12
+ import { AxiosPromise, AxiosInstance, AxiosRequestConfig } from 'axios';
13
+ import { Configuration } from '../configuration';
14
+ import { RequestArgs, BaseAPI } from '../base';
15
+ import { InlineResponse200 } from '../models';
16
+ /**
17
+ * DefaultApi - axios parameter creator
18
+ * @export
19
+ */
20
+ export declare const DefaultApiAxiosParamCreator: (configuration?: Configuration) => {
21
+ /**
22
+ * Returns the health status of the translation service. This endpoint is used to monitor the operational status of the translation service. It typically returns a simple status indicator, such as \'UP\' or \'OK\', confirming that the service is operational and available.
23
+ * @summary Health Check
24
+ * @param {*} [options] Override http request option.
25
+ * @throws {RequiredError}
26
+ */
27
+ check: (options?: AxiosRequestConfig) => Promise<RequestArgs>;
28
+ };
29
+ /**
30
+ * DefaultApi - functional programming interface
31
+ * @export
32
+ */
33
+ export declare const DefaultApiFp: (configuration?: Configuration) => {
34
+ /**
35
+ * Returns the health status of the translation service. This endpoint is used to monitor the operational status of the translation service. It typically returns a simple status indicator, such as \'UP\' or \'OK\', confirming that the service is operational and available.
36
+ * @summary Health Check
37
+ * @param {*} [options] Override http request option.
38
+ * @throws {RequiredError}
39
+ */
40
+ check(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse200>>;
41
+ };
42
+ /**
43
+ * DefaultApi - factory interface
44
+ * @export
45
+ */
46
+ export declare const DefaultApiFactory: (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) => {
47
+ /**
48
+ * Returns the health status of the translation service. This endpoint is used to monitor the operational status of the translation service. It typically returns a simple status indicator, such as \'UP\' or \'OK\', confirming that the service is operational and available.
49
+ * @summary Health Check
50
+ * @param {*} [options] Override http request option.
51
+ * @throws {RequiredError}
52
+ */
53
+ check(options?: any): AxiosPromise<InlineResponse200>;
54
+ };
55
+ /**
56
+ * DefaultApi - object-oriented interface
57
+ * @export
58
+ * @class DefaultApi
59
+ * @extends {BaseAPI}
60
+ */
61
+ export declare class DefaultApi extends BaseAPI {
62
+ /**
63
+ * Returns the health status of the translation service. This endpoint is used to monitor the operational status of the translation service. It typically returns a simple status indicator, such as \'UP\' or \'OK\', confirming that the service is operational and available.
64
+ * @summary Health Check
65
+ * @param {*} [options] Override http request option.
66
+ * @throws {RequiredError}
67
+ * @memberof DefaultApi
68
+ */
69
+ check(options?: AxiosRequestConfig): Promise<import("axios").AxiosResponse<InlineResponse200, any, {}>>;
70
+ }