attlaz-client 1.47.3 → 1.48.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/dist/Client.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { OAuthClient } from './Http/OAuthClient.js';
2
1
  import { OAuthClientToken } from './Http/OAuthClientToken.js';
2
+ import { ITransport } from './Http/Transport/ITransport.js';
3
+ import { OAuthClient } from './Http/Transport/OAuthClient.js';
3
4
  import { AccessTokenEndpoint } from './Service/AccessTokenEndpoint.js';
4
5
  import { AdapterConnectionEndpoint } from './Service/AdapterConnectionEndpoint.js';
5
6
  import { AdapterEndpoint } from './Service/AdapterEndpoint.js';
@@ -36,12 +37,19 @@ import { WorkspaceMemberEndpoint } from './Service/WorkspaceMemberEndpoint.js';
36
37
  export declare class Client {
37
38
  private readonly endpoints;
38
39
  private httpClient;
40
+ private transport;
39
41
  private readonly Store;
40
42
  private readonly apiEndpoint;
41
- private parseConfig;
42
43
  constructor(token?: string | null, config?: {
43
- apiEndpoint: string | undefined;
44
+ apiEndpoint?: string;
44
45
  });
46
+ /**
47
+ * Create a Client that uses a custom transport instead of OAuth.
48
+ * Intended for internal service-to-service calls (e.g. using DirectTransport).
49
+ */
50
+ static withTransport(transport: ITransport, config?: {
51
+ apiEndpoint: string | undefined;
52
+ }): Client;
45
53
  setClientCredentials(clientId: string, clientSecret?: string): void;
46
54
  setVersion(version: string | null): void;
47
55
  getHttpClient(): OAuthClient;
package/dist/Client.js CHANGED
@@ -1,6 +1,6 @@
1
- import { OAuthClient } from './Http/OAuthClient.js';
2
1
  import { OAuthClientOptions } from './Http/OAuthClientOptions.js';
3
2
  import { OAuthClientToken } from './Http/OAuthClientToken.js';
3
+ import { OAuthClient } from './Http/Transport/OAuthClient.js';
4
4
  import { AccessTokenEndpoint } from './Service/AccessTokenEndpoint.js';
5
5
  import { AdapterConnectionEndpoint } from './Service/AdapterConnectionEndpoint.js';
6
6
  import { AdapterEndpoint } from './Service/AdapterEndpoint.js';
@@ -36,8 +36,9 @@ import { WorkspaceEndpoint } from './Service/WorkspaceEndpoint.js';
36
36
  import { WorkspaceMemberEndpoint } from './Service/WorkspaceMemberEndpoint.js';
37
37
  import { VERSION } from './version.js';
38
38
  export class Client {
39
- endpoints;
39
+ endpoints = new Map();
40
40
  httpClient;
41
+ transport;
41
42
  Store = {
42
43
  AccessTokenEndpoint,
43
44
  AdapterConnectionEndpoint,
@@ -74,14 +75,9 @@ export class Client {
74
75
  ProviderTokenEndpoint,
75
76
  };
76
77
  apiEndpoint = 'https://api.attlaz.com';
77
- parseConfig(config) {
78
- // TODO: go over other records in config and warn if any unknown ones
79
- return config;
80
- }
81
- constructor(token = null, config = { apiEndpoint: undefined }) {
82
- const parsedConfig = this.parseConfig(config);
83
- if (parsedConfig.apiEndpoint !== undefined) {
84
- this.apiEndpoint = parsedConfig.apiEndpoint;
78
+ constructor(token = null, config = {}) {
79
+ if (config.apiEndpoint !== undefined) {
80
+ this.apiEndpoint = config.apiEndpoint;
85
81
  }
86
82
  const options = new OAuthClientOptions(this.apiEndpoint);
87
83
  this.httpClient = new OAuthClient(options);
@@ -89,7 +85,16 @@ export class Client {
89
85
  const clientToken = new OAuthClientToken(token, 'Bearer', '', '');
90
86
  this.httpClient.setToken(clientToken);
91
87
  }
92
- this.endpoints = new Map();
88
+ this.transport = this.httpClient;
89
+ }
90
+ /**
91
+ * Create a Client that uses a custom transport instead of OAuth.
92
+ * Intended for internal service-to-service calls (e.g. using DirectTransport).
93
+ */
94
+ static withTransport(transport, config = { apiEndpoint: undefined }) {
95
+ const client = new Client(null, config);
96
+ client.transport = transport;
97
+ return client;
93
98
  }
94
99
  setClientCredentials(clientId, clientSecret = '') {
95
100
  const options = new OAuthClientOptions(this.apiEndpoint, clientId, clientSecret);
@@ -97,6 +102,7 @@ export class Client {
97
102
  options.scopes = ['all'];
98
103
  // TODO: do we need to create a new oauth client, or can we update with these credentials?
99
104
  this.httpClient = new OAuthClient(options);
105
+ this.transport = this.httpClient;
100
106
  }
101
107
  setVersion(version) {
102
108
  this.httpClient.setVersion(version);
@@ -204,7 +210,7 @@ export class Client {
204
210
  return this.getEndpoint('configuration', this.Store.ConfigurationEndpoint);
205
211
  }
206
212
  getHealthAlertEndpoint() {
207
- return this.getEndpoint('healt-alert', this.Store.HealthAlertEndpoint);
213
+ return this.getEndpoint('health-alert', this.Store.HealthAlertEndpoint);
208
214
  }
209
215
  getCodeSourceStrategiesEndpoint() {
210
216
  return this.getEndpoint('code-source-strategies', this.Store.CodeSourceStrategiesEndpoint);
@@ -227,22 +233,17 @@ export class Client {
227
233
  getProviderTokenEndpoint() {
228
234
  return this.getEndpoint('provider_token', this.Store.ProviderTokenEndpoint);
229
235
  }
230
- // TODO: this should be in a separate API?
231
236
  getFirewallEndpoint() {
232
237
  return this.getEndpoint('firewall', this.Store.FirewallEndpoint);
233
238
  }
234
239
  getEndpoint(key, ClassName) {
235
- // const classNameString: string = className.name;
236
240
  if (!this.endpoints.has(key)) {
237
- // if (this.Store[classNameString] === undefined || this.Store[classNameString] === null) {
238
- // throw new Error(`Class type of \'${classNameString}\' is not in the store`);
239
- // }
240
241
  try {
241
- const endpoint = new ClassName(this.httpClient);
242
- this.endpoints.set(key, endpoint);
242
+ this.endpoints.set(key, new ClassName(this.transport));
243
243
  }
244
244
  catch (error) {
245
- throw new Error('Unable to initialize ' + key + ' endpoint (' + error.message + ')');
245
+ const errorMsg = (error !== null && typeof error === 'object' && 'message' in error && typeof error.message === 'string') ? error.message : 'Unknown';
246
+ throw new Error('Unable to initialize ' + key + ' endpoint (' + errorMsg + ')');
246
247
  }
247
248
  }
248
249
  return this.endpoints.get(key);
@@ -14,7 +14,6 @@ export class ClientError extends Error {
14
14
  if (error instanceof AxiosError) {
15
15
  if (error.code === 'ECONNREFUSED' || error.code === 'ERR_NETWORK') {
16
16
  clientError.httpStatus = HttpStatus.HTTP_UNAVAILABLE;
17
- // clientError.code = 'Service not available';
18
17
  clientError.message = 'Service not available';
19
18
  return clientError;
20
19
  }
@@ -24,9 +23,6 @@ export class ClientError extends Error {
24
23
  // console.log('Status', clientError.httpStatus);
25
24
  // if(clientError.httpStatus === HttpStatusCode.Ref)
26
25
  // clientError.message = error.message;
27
- if (error.code !== undefined) {
28
- // clientError.code = error.code;
29
- }
30
26
  if (error.response === undefined) {
31
27
  console.error('Unknown AxiosError error', {
32
28
  error, status: error.status, code: error.code,
@@ -43,15 +39,6 @@ export class ClientError extends Error {
43
39
  }
44
40
  return clientError;
45
41
  }
46
- // if (error.body !== undefined && error.body !== null && error.body.error !== undefined && error.body.error !== null) {
47
- // error = error.body.error;
48
- // }
49
- // let message: string = error;
50
- // if (error.message !== undefined && error.message !== null) {
51
- // message = error.message;
52
- // }
53
- //
54
- // console.error(error);
55
42
  const xError = error;
56
43
  if (xError.status !== null && xError.status !== undefined) {
57
44
  const statusCode = xError.status;
@@ -1,6 +1,7 @@
1
1
  import { HttpClientRequest } from './HttpClientRequest.js';
2
2
  import { HttpClientResponse } from './HttpClientResponse.js';
3
3
  export declare class HttpClient {
4
+ private static readonly axiosInstance;
4
5
  static request(request: HttpClientRequest): Promise<HttpClientResponse>;
5
6
  private static axiosRequest;
6
7
  }
@@ -2,18 +2,9 @@ import axios from 'axios';
2
2
  import { HttpClientResponse } from './HttpClientResponse.js';
3
3
  import { ClientError } from './ClientError.js';
4
4
  export class HttpClient {
5
+ static axiosInstance = axios.create();
5
6
  static async request(request) {
6
- const response = await HttpClient.axiosRequest(request);
7
- // const error: ClientError | null = ClientError.byStatus(response.status, response.statusText);
8
- // if (error !== null && error !== undefined) {
9
- // if (response.body !== null && response.body !== undefined && response.body.error !== null && response.body.error !== undefined && response.body.error.message !== null && response.body.error.message !== undefined
10
- // ) {
11
- // error.message = response.body.error.message;
12
- // }
13
- //
14
- // throw error;
15
- // }
16
- return response;
7
+ return HttpClient.axiosRequest(request);
17
8
  }
18
9
  static async axiosRequest(request) {
19
10
  const requestConfig = {
@@ -24,29 +15,17 @@ export class HttpClient {
24
15
  if (request.body !== null && request.body !== undefined) {
25
16
  requestConfig.data = request.body;
26
17
  }
27
- // TODO: should we share this instance between requests?
28
- const axiosInstance = axios.create();
29
- // TODo: should we catch axios error sand make them into ClientErrors?
30
18
  try {
31
- const rawResponse = await axiosInstance.request(requestConfig);
19
+ const rawResponse = await HttpClient.axiosInstance.request(requestConfig);
32
20
  const httpResponse = new HttpClientResponse(rawResponse.status, rawResponse.statusText);
33
21
  Object.keys(rawResponse.headers).forEach((header) => {
34
22
  const headerValue = rawResponse.headers[header];
35
23
  httpResponse.headers[header] = headerValue === null ? '' : headerValue;
36
24
  });
37
- httpResponse.body = await rawResponse.data;
25
+ httpResponse.body = rawResponse.data;
38
26
  return httpResponse;
39
27
  }
40
28
  catch (requestError) {
41
- // if (requestError instanceof AxiosError) {
42
- // const status: number = requestError.status === undefined ? 500 : requestError.status;
43
- // const statusCode: string = requestError.code === undefined ? 'Error' : requestError.code;
44
- //
45
- // const response: HttpClientResponse = new HttpClientResponse(status, statusCode);
46
- //
47
- //
48
- // throw ClientError.fromError(requestError);
49
- // }
50
29
  throw ClientError.fromError(requestError);
51
30
  }
52
31
  }
@@ -0,0 +1,29 @@
1
+ import { Parameters } from '../Data/Parameters.js';
2
+ import { ITransport } from './ITransport.js';
3
+ export interface DirectTransportRoute {
4
+ prefix: string;
5
+ baseUrl: string;
6
+ }
7
+ /**
8
+ * Transport for internal service-to-service calls that bypasses Gateway OAuth.
9
+ * Authenticates via HMAC-signed session headers instead of an OAuth token.
10
+ *
11
+ * Usage:
12
+ * const transport = new DirectTransport('http://core-api:5728', process.env.api_secret);
13
+ * const client = Client.withTransport(transport);
14
+ * client.getFlowEndpoint().getById(id);
15
+ *
16
+ * With multiple backends:
17
+ * const transport = new DirectTransport('http://core-api:5728', secret, [
18
+ * { prefix: '/products', baseUrl: 'http://products-api:5730' },
19
+ * { prefix: '/services', baseUrl: 'http://services-api:5731' },
20
+ * ]);
21
+ */
22
+ export declare class DirectTransport implements ITransport {
23
+ private readonly clients;
24
+ private readonly sortedRoutes;
25
+ constructor(defaultBaseUrl: string, apiSecret: string, routes?: DirectTransportRoute[]);
26
+ request<T>(action: string, parameters?: Parameters, method?: string, _signWithOauthToken?: boolean): Promise<T>;
27
+ private resolveClient;
28
+ isDebugEnabled(): boolean;
29
+ }
@@ -0,0 +1,65 @@
1
+ import { createHmac } from 'node:crypto';
2
+ import { OAuthClientOptions } from '../OAuthClientOptions.js';
3
+ import { OAuthClient } from './OAuthClient.js';
4
+ /**
5
+ * Transport for internal service-to-service calls that bypasses Gateway OAuth.
6
+ * Authenticates via HMAC-signed session headers instead of an OAuth token.
7
+ *
8
+ * Usage:
9
+ * const transport = new DirectTransport('http://core-api:5728', process.env.api_secret);
10
+ * const client = Client.withTransport(transport);
11
+ * client.getFlowEndpoint().getById(id);
12
+ *
13
+ * With multiple backends:
14
+ * const transport = new DirectTransport('http://core-api:5728', secret, [
15
+ * { prefix: '/products', baseUrl: 'http://products-api:5730' },
16
+ * { prefix: '/services', baseUrl: 'http://services-api:5731' },
17
+ * ]);
18
+ */
19
+ export class DirectTransport {
20
+ clients = new Map();
21
+ sortedRoutes;
22
+ constructor(defaultBaseUrl, apiSecret, routes = []) {
23
+ // TODO: do we need to define user, client and ip?
24
+ const sessionPayload = JSON.stringify({
25
+ api_version: 'latest',
26
+ user: null,
27
+ client: null,
28
+ ip: 'internal',
29
+ roles: [],
30
+ scopes: [],
31
+ access_token: null,
32
+ });
33
+ const sessionSignature = createHmac('sha256', apiSecret).update(sessionPayload).digest('hex');
34
+ // All routes plus the catch-all default, sorted by prefix length descending (most specific first)
35
+ this.sortedRoutes = [...routes, { prefix: '', baseUrl: defaultBaseUrl }]
36
+ .sort((a, b) => b.prefix.length - a.prefix.length);
37
+ // One OAuthClient per unique base URL with session headers pre-injected
38
+ const allBaseUrls = new Set([defaultBaseUrl, ...routes.map(r => r.baseUrl)]);
39
+ for (const baseUrl of allBaseUrls) {
40
+ const client = new OAuthClient(new OAuthClientOptions(baseUrl));
41
+ client.addDefaultHeader('x-session', sessionPayload);
42
+ client.addDefaultHeader('x-session-signature', sessionSignature);
43
+ this.clients.set(baseUrl, client);
44
+ }
45
+ }
46
+ async request(action, parameters = null, method = 'GET', _signWithOauthToken = true) {
47
+ const client = this.resolveClient(action);
48
+ // Always call without OAuth signing — authentication is via the signed session headers
49
+ return await client.request(action, parameters, method, false);
50
+ }
51
+ resolveClient(action) {
52
+ for (const route of this.sortedRoutes) {
53
+ if (action.startsWith(route.prefix)) {
54
+ const client = this.clients.get(route.baseUrl);
55
+ if (client !== undefined) {
56
+ return client;
57
+ }
58
+ }
59
+ }
60
+ throw new Error('No transport route found for action: ' + action);
61
+ }
62
+ isDebugEnabled() {
63
+ return false;
64
+ }
65
+ }
@@ -0,0 +1,5 @@
1
+ import { Parameters } from '../Data/Parameters.js';
2
+ export interface ITransport {
3
+ request: <T>(action: string, parameters: Parameters, method: string, signWithOauthToken: boolean) => Promise<T>;
4
+ isDebugEnabled: () => boolean;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,12 +1,13 @@
1
- import { Headers } from './Data/Headers.js';
2
- import { Parameters } from './Data/Parameters.js';
3
- import { OAuthClientOptions } from './OAuthClientOptions.js';
4
- import { OAuthClientToken } from './OAuthClientToken.js';
5
- export declare class OAuthClient {
1
+ import { Headers } from '../Data/Headers.js';
2
+ import { Parameters } from '../Data/Parameters.js';
3
+ import { ITransport } from './ITransport.js';
4
+ import { OAuthClientOptions } from '../OAuthClientOptions.js';
5
+ import { OAuthClientToken } from '../OAuthClientToken.js';
6
+ export declare class OAuthClient implements ITransport {
6
7
  private readonly options;
7
8
  private debug;
8
9
  private axiosInstance;
9
- private oathClientToken;
10
+ private oauthClientToken;
10
11
  private refreshTokenPromise;
11
12
  private version;
12
13
  constructor(options: OAuthClientOptions);
@@ -15,7 +16,7 @@ export declare class OAuthClient {
15
16
  refreshToken(): Promise<void>;
16
17
  private getAxiosInstance;
17
18
  isTokenExpires(): boolean;
18
- request(action: string, parameters?: Parameters, method?: string, signWithOauthToken?: boolean): Promise<any>;
19
+ request<T>(action: string, parameters?: Parameters, method?: string, signWithOauthToken?: boolean): Promise<T>;
19
20
  isAuthenticated(): boolean;
20
21
  getToken(): OAuthClientToken | null;
21
22
  setToken(token: OAuthClientToken): void;
@@ -1,16 +1,16 @@
1
1
  import axios from 'axios';
2
2
  import { clientCredentials, ownerCredentials, refreshToken } from 'axios-oauth-client';
3
- import { JsonSerializable } from '../Model/JsonSerializable.js';
4
- import { VERSION } from '../version.js';
5
- import { ClientError } from './ClientError.js';
6
- import { HttpClient } from './HttpClient.js';
7
- import { HttpClientRequest } from './HttpClientRequest.js';
8
- import { OAuthClientToken } from './OAuthClientToken.js';
3
+ import { JsonSerializable } from '../../Model/JsonSerializable.js';
4
+ import { VERSION } from '../../version.js';
5
+ import { ClientError } from '../ClientError.js';
6
+ import { HttpClient } from '../HttpClient.js';
7
+ import { HttpClientRequest } from '../HttpClientRequest.js';
8
+ import { OAuthClientToken } from '../OAuthClientToken.js';
9
9
  export class OAuthClient {
10
10
  options;
11
11
  debug = false;
12
12
  axiosInstance = null;
13
- oathClientToken = null;
13
+ oauthClientToken = null;
14
14
  refreshTokenPromise = null;
15
15
  version = null;
16
16
  constructor(options) {
@@ -24,24 +24,15 @@ export class OAuthClient {
24
24
  // Password flow / Owner Credentials grant
25
25
  const getOwnerCredentials = ownerCredentials(this.getAxiosInstance(), this.getApiEndpointUrl(this.options.accessTokenUri), this.options.clientId === null ? undefined : this.options.clientId, this.options.clientSecret === null ? undefined : this.options.clientSecret);
26
26
  const rawAuthToken = await getOwnerCredentials(username, password, this.options.scopes.join(' '));
27
- this.oathClientToken = this.tokenToOauthClientToken(rawAuthToken);
27
+ this.oauthClientToken = this.tokenToOauthClientToken(rawAuthToken);
28
28
  return true;
29
29
  }
30
30
  if (this.options.clientId !== null && this.options.clientSecret !== null) {
31
31
  // Client credentials flow
32
32
  const getClientCredentials = clientCredentials(this.getAxiosInstance(), this.getApiEndpointUrl(this.options.accessTokenUri), this.options.clientId === null ? undefined : this.options.clientId, this.options.clientSecret === null ? undefined : this.options.clientSecret);
33
33
  const rawAuthToken = await getClientCredentials(this.options.scopes.join(' '));
34
- // console.log('Raw token', rawAuthToken);
35
- this.oathClientToken = this.tokenToOauthClientToken(rawAuthToken);
34
+ this.oauthClientToken = this.tokenToOauthClientToken(rawAuthToken);
36
35
  }
37
- // const getRefreshToken = refreshToken(
38
- // this.getAxiosInstance(),
39
- // this.getApiEndpointUrl(this.options.accessTokenUri),
40
- // this.options.clientId === null ? undefined : this.options.clientId,
41
- // this.options.clientSecret === null ? undefined : this.options.clientSecret,
42
- // );
43
- // const auth = await getRefreshToken(this.oathClientToken.access_token, this.options.scopes.join(' '))
44
- // console.log('Refresh token', auth);
45
36
  return true;
46
37
  }
47
38
  catch (requestError) {
@@ -55,16 +46,16 @@ export class OAuthClient {
55
46
  if (this.debug) {
56
47
  console.debug('[OAuthClient] refresh token');
57
48
  }
58
- if (this.oathClientToken === null) {
49
+ if (this.oauthClientToken === null) {
59
50
  throw new Error('unable to refresh token, auth token not set');
60
51
  }
61
- if (this.oathClientToken.refresh_token === undefined || this.oathClientToken.refresh_token === null || this.oathClientToken.refresh_token === '') {
52
+ if (this.oauthClientToken.refresh_token === undefined || this.oauthClientToken.refresh_token === null || this.oauthClientToken.refresh_token === '') {
62
53
  throw new Error('unable to refresh token, refresh token not set');
63
54
  }
64
55
  try {
65
56
  const getRefreshToken = refreshToken(this.getAxiosInstance(), this.getApiEndpointUrl(this.options.accessTokenUri), this.options.clientId === null ? undefined : this.options.clientId, this.options.clientSecret === null ? undefined : this.options.clientSecret);
66
- const auth = await getRefreshToken(this.oathClientToken.refresh_token, this.options.scopes.join(' '));
67
- this.oathClientToken = this.tokenToOauthClientToken(auth);
57
+ const auth = await getRefreshToken(this.oauthClientToken.refresh_token, this.options.scopes.join(' '));
58
+ this.oauthClientToken = this.tokenToOauthClientToken(auth);
68
59
  }
69
60
  catch (e) {
70
61
  throw ClientError.fromError(e);
@@ -82,10 +73,10 @@ export class OAuthClient {
82
73
  return this.axiosInstance;
83
74
  }
84
75
  isTokenExpires() {
85
- if (this.oathClientToken === null) {
76
+ if (this.oauthClientToken === null) {
86
77
  throw new Error('No token defined');
87
78
  }
88
- return OAuthClientToken.isExpired(this.oathClientToken);
79
+ return OAuthClientToken.isExpired(this.oauthClientToken);
89
80
  }
90
81
  async request(action, parameters = null, method = 'GET', signWithOauthToken = true) {
91
82
  if (signWithOauthToken && !this.isAuthenticated()) {
@@ -93,10 +84,10 @@ export class OAuthClient {
93
84
  }
94
85
  else {
95
86
  if (signWithOauthToken) {
96
- if (this.oathClientToken === null) {
87
+ if (this.oauthClientToken === null) {
97
88
  throw new ClientError('Unable to perform request, access token not provided');
98
89
  }
99
- if (signWithOauthToken && OAuthClientToken.isExpired(this.oathClientToken)) {
90
+ if (signWithOauthToken && OAuthClientToken.isExpired(this.oauthClientToken)) {
100
91
  if (this.refreshTokenPromise === null) {
101
92
  this.refreshTokenPromise = this.refreshToken();
102
93
  await this.refreshTokenPromise;
@@ -113,9 +104,6 @@ export class OAuthClient {
113
104
  }
114
105
  try {
115
106
  const response = await HttpClient.request(requestData);
116
- // if (this.debug) {
117
- // console.info('[Client response] ', {body: response.body});
118
- // }
119
107
  return response.body;
120
108
  }
121
109
  catch (error) {
@@ -135,16 +123,16 @@ export class OAuthClient {
135
123
  }
136
124
  isAuthenticated() {
137
125
  // TODO: other ways to determine if the client is authenticated?
138
- return this.oathClientToken !== null && this.oathClientToken !== undefined;
126
+ return this.oauthClientToken !== null && this.oauthClientToken !== undefined;
139
127
  }
140
128
  getToken() {
141
- return this.oathClientToken;
129
+ return this.oauthClientToken;
142
130
  }
143
131
  setToken(token) {
144
- this.oathClientToken = token;
132
+ this.oauthClientToken = token;
145
133
  }
146
134
  unsetToken() {
147
- this.oathClientToken = null;
135
+ this.oauthClientToken = null;
148
136
  }
149
137
  enableDebug() {
150
138
  this.debug = true;
@@ -229,10 +217,10 @@ export class OAuthClient {
229
217
  });
230
218
  }
231
219
  if (signWithOauthToken) {
232
- if (this.oathClientToken === null) {
220
+ if (this.oauthClientToken === null) {
233
221
  throw new ClientError('Unable to perform request, access token not provided');
234
222
  }
235
- if (OAuthClientToken.isExpired(this.oathClientToken)) {
223
+ if (OAuthClientToken.isExpired(this.oauthClientToken)) {
236
224
  throw new Error('Unable to sign request, token is expired');
237
225
  }
238
226
  requestData = this.signRequest(requestData);
@@ -240,23 +228,16 @@ export class OAuthClient {
240
228
  return requestData;
241
229
  }
242
230
  signRequest(requestObject) {
243
- if (this.oathClientToken === null) {
231
+ if (this.oauthClientToken === null) {
244
232
  throw new ClientError('Unable to sign request, access token not provided');
245
233
  }
246
- if (OAuthClientToken.isExpired(this.oathClientToken)) {
247
- throw new Error('Unable to sign request, token is expired');
248
- }
249
- const tokenType = this.oathClientToken.token_type;
250
- const accessToken = this.oathClientToken.access_token;
234
+ const tokenType = this.oauthClientToken.token_type;
235
+ const accessToken = this.oauthClientToken.access_token;
251
236
  if (tokenType !== undefined && tokenType.toLowerCase() === 'bearer') {
252
237
  requestObject.setHeader('Authorization', 'Bearer ' + accessToken);
253
238
  }
254
239
  else {
255
- requestObject.addQueryParam('access_token', accessToken);
256
- // Attempt to avoid storing the url in proxies, since the access token
257
- // is exposed in the query parameters.
258
- requestObject.setHeader('Pragma', 'no-store');
259
- requestObject.setHeader('Cache-Control', 'no-store');
240
+ throw new ClientError('Unsupported token type "' + (tokenType ?? 'undefined') + '": only Bearer tokens are supported');
260
241
  }
261
242
  return requestObject;
262
243
  }
@@ -1,11 +1,11 @@
1
1
  import { Parameters } from '../Http/Data/Parameters.js';
2
2
  import { QueryString } from '../Http/Data/QueryString.js';
3
- import { OAuthClient } from '../Http/OAuthClient.js';
3
+ import { ITransport } from '../Http/Transport/ITransport.js';
4
4
  import { CollectionResult } from '../Model/Result/CollectionResult.js';
5
5
  import { ObjectResult } from '../Model/Result/ObjectResult.js';
6
6
  export declare abstract class Endpoint {
7
- protected httpClient: OAuthClient;
8
- constructor(httpClient: OAuthClient);
7
+ protected httpClient: ITransport;
8
+ constructor(httpClient: ITransport);
9
9
  private formatParameters;
10
10
  private formatKey;
11
11
  requestCollection<T>(action: string | QueryString, parser: (input: any) => T, parameters?: Parameters, method?: string, signWithOauthToken?: boolean): Promise<CollectionResult<T>>;
@@ -3,7 +3,6 @@ import { QueryString } from '../Http/Data/QueryString.js';
3
3
  import { HttpStatus } from '../Http/HttpStatus.js';
4
4
  import { DataValueCollection } from '../Model/DataValueCollection.js';
5
5
  import { ApiError } from '../Model/Error/ApiError.js';
6
- import { JsonSerializable } from '../Model/JsonSerializable.js';
7
6
  import { LogStreamId } from '../Model/Log/LogStreamId.js';
8
7
  import { CollectionResult } from '../Model/Result/CollectionResult.js';
9
8
  import { ObjectResult } from '../Model/Result/ObjectResult.js';
@@ -32,28 +31,19 @@ export class Endpoint {
32
31
  if (Utils.isDate(data)) {
33
32
  return data.toISOString();
34
33
  }
35
- // tODo: this does not seem to work correclty
36
- if (data instanceof JsonSerializable && 'jsonSerialize' in data) {
37
- return data.jsonSerialize();
38
- }
39
34
  if (typeof data === 'object') {
40
- if (
41
- // data instanceof WorkspaceId ||
42
- // data instanceof ProjectEnvironmentId ||
43
- // data instanceof ProjectId ||
44
- // data instanceof OrganisationId ||
45
- // data instanceof FlowId ||
46
- // data instanceof FlowRunId ||
47
- // data instanceof UserId ||
48
- // data instanceof DataCollectionResult ||
49
- // data instanceof SuccessResult ||
50
- data instanceof DataValueCollection
51
- || data instanceof LogStreamId) {
35
+ // These types need recursive formatting applied to their serialized output.
36
+ if (data instanceof DataValueCollection || data instanceof LogStreamId) {
52
37
  return this.formatParameters(data.jsonSerialize());
53
38
  }
54
39
  if (data instanceof QueryString) {
55
40
  return data.getParameters();
56
41
  }
42
+ // Duck-type check for JsonSerializable: avoids fragile instanceof checks across
43
+ // ESM module boundaries where class identity can differ between realms.
44
+ if (typeof data.jsonSerialize === 'function') {
45
+ return data.jsonSerialize();
46
+ }
57
47
  const result = {};
58
48
  for (const [key, value] of Object.entries(data)) {
59
49
  const formattedKey = this.formatKey(key);
@@ -66,11 +56,14 @@ export class Endpoint {
66
56
  formatKey(key) {
67
57
  let formattedKey = key.replace(/([A-Z])/g, ' $1');
68
58
  formattedKey = formattedKey.split(' ').join('_').toLowerCase();
59
+ // Intentional: the API expects bare resource names, not ID-suffixed ones.
60
+ // e.g. TypeScript `environmentId` → snake_case `environment_id` → API param `environment`
69
61
  if (formattedKey.endsWith('_id')) {
70
62
  formattedKey = formattedKey.substring(0, formattedKey.length - '_id'.length);
71
63
  }
64
+ // Intentional: same convention for plural IDs.
65
+ // e.g. `categoryIds` → `category_ids` → `categorys` (TODO: handle irregular plurals properly)
72
66
  if (formattedKey.endsWith('_ids')) {
73
- // TODO: update this to format correct plurals (for example category_ids should become categories instead of categorys)
74
67
  formattedKey = formattedKey.substring(0, formattedKey.length - '_ids'.length) + 's';
75
68
  }
76
69
  return formattedKey;
@@ -88,10 +81,6 @@ export class Endpoint {
88
81
  throw this.toApiError(error);
89
82
  }
90
83
  const result = new CollectionResult();
91
- /**
92
- * Parse errors
93
- */
94
- // DataResult.appendErrors(result, requestResponse);
95
84
  /**
96
85
  * Parse data
97
86
  */
@@ -125,11 +114,6 @@ export class Endpoint {
125
114
  }
126
115
  throw this.toApiError(error);
127
116
  }
128
- // TODO: add errors
129
- /**
130
- * Parse errors
131
- */
132
- // TODO: temporary check until we know the API is fully upgraded
133
117
  if (requestResponse === null || requestResponse === undefined) {
134
118
  throw new ApiError('Unable to parse object: response is empty for action `[' + method + '] ' + action + '`', HttpStatus.HTTP_INTERNAL_SERVER_ERROR);
135
119
  }
@@ -146,7 +130,6 @@ export class Endpoint {
146
130
  if (error.response !== null && error.response !== undefined) {
147
131
  const response = error.response;
148
132
  if (response.data.error !== undefined) {
149
- // console.debug('Error data', errorData);
150
133
  if (response.data.error.type !== undefined) {
151
134
  apiError.type = response.data.error.type;
152
135
  }
@@ -163,15 +146,11 @@ export class Endpoint {
163
146
  }
164
147
  return apiError;
165
148
  }
166
- // TODO: remove this
167
- console.log('Not client error', { error });
149
+ if (this.httpClient.isDebugEnabled()) {
150
+ console.error('Unrecognised error type (not a ClientError)', { error });
151
+ }
168
152
  return new ApiError('Unknown Error', HttpStatus.HTTP_INTERNAL_SERVER_ERROR);
169
153
  }
170
- // public async request<T>(action: string, parameters: any | null = null, method: string = 'GET', signWithOauthToken: boolean = true): Promise<DataResult<T>> {
171
- // const result: any = await this.httpClient.request(action, parameters, method, signWithOauthToken);
172
- //
173
- // return DataResult.parse<T>(result);
174
- // }
175
154
  parseCollection(rawData, parser) {
176
155
  const data = [];
177
156
  if (!Array.isArray(rawData)) {
@@ -186,7 +165,6 @@ export class Endpoint {
186
165
  return data;
187
166
  }
188
167
  parseObject(rawObject, parser) {
189
- // TODO: is it interesting to keep this wrapper, or only in develop mode?
190
168
  const wrappedData = ObjectWrapper.wrap(rawObject);
191
169
  try {
192
170
  return parser(wrappedData);
@@ -1,12 +1,12 @@
1
1
  import { Flow } from '../Model/Flow/Flow.js';
2
2
  import { FlowSummary } from '../Model/Flow/FlowSummary.js';
3
- import { CollectionResult } from '../Model/Result/CollectionResult.js';
4
3
  import { CursorPagination } from '../Model/Pagination/CursorPagination.js';
4
+ import { CollectionResult } from '../Model/Result/CollectionResult.js';
5
5
  import { Endpoint } from './Endpoint.js';
6
6
  export declare class FlowEndpoint extends Endpoint {
7
7
  getByProject(projectId: string, pagination: CursorPagination): Promise<CollectionResult<Flow>>;
8
8
  getById(flowId: string): Promise<Flow | null>;
9
- getFlowSummaries(projectId: string, projectEnvironmentId?: string | null): Promise<CollectionResult<FlowSummary>>;
9
+ getFlowSummaries(projectId: string, projectEnvironmentId?: string | null, pagination?: CursorPagination | null): Promise<CollectionResult<FlowSummary>>;
10
10
  getFlowSummary(flowId: string, projectEnvironmentId?: string | null): Promise<FlowSummary | null>;
11
11
  save(flow: Flow): Promise<Flow>;
12
12
  }
@@ -1,8 +1,8 @@
1
- import { Utils } from '../Utils.js';
1
+ import { QueryString } from '../Http/Data/QueryString.js';
2
+ import { HttpStatus } from '../Http/HttpStatus.js';
2
3
  import { Flow } from '../Model/Flow/Flow.js';
3
4
  import { FlowSummary } from '../Model/Flow/FlowSummary.js';
4
- import { HttpStatus } from '../Http/HttpStatus.js';
5
- import { QueryString } from '../Http/Data/QueryString.js';
5
+ import { Utils } from '../Utils.js';
6
6
  import { Endpoint } from './Endpoint.js';
7
7
  export class FlowEndpoint extends Endpoint {
8
8
  async getByProject(projectId, pagination) {
@@ -34,22 +34,16 @@ export class FlowEndpoint extends Endpoint {
34
34
  throw error;
35
35
  }
36
36
  }
37
- async getFlowSummaries(projectId, projectEnvironmentId = null) {
37
+ async getFlowSummaries(projectId, projectEnvironmentId = null, pagination = null) {
38
38
  let parameters = null;
39
39
  if (!Utils.isNullOrUndefined(projectEnvironmentId)) {
40
40
  parameters = { environment: projectEnvironmentId };
41
41
  }
42
- const result = await this.requestCollection('projects/' + projectId + '/flowsummaries', FlowSummary.parse, parameters);
43
- return result;
44
- //
45
- // }).catch((reason: any) => {
46
- // if (this.httpClient.isDebugEnabled()) {
47
- // console.error('Failed to load flow summaries: ' + reason);
48
- // }
49
- //
50
- // reject(reason);
51
- // });
52
- // });
42
+ const queryString = new QueryString('projects/' + projectId + '/flowsummaries');
43
+ if (pagination !== null) {
44
+ queryString.addPagination(pagination);
45
+ }
46
+ return await this.requestCollection(queryString, FlowSummary.parse, parameters);
53
47
  }
54
48
  async getFlowSummary(flowId, projectEnvironmentId = null) {
55
49
  let parameters = null;
@@ -32,7 +32,6 @@ export class StorageEndpoint extends Endpoint {
32
32
  const result = await this.requestObject(cmd, null, parser, 'DELETE');
33
33
  const re = result.getData();
34
34
  if (re === null) {
35
- console.log('clearpool wrong data', { result, parsed: re });
36
35
  throw new Error('Unable to clear storage pool: wrong response from API');
37
36
  }
38
37
  return re.deleted;
@@ -90,7 +89,6 @@ export class StorageEndpoint extends Endpoint {
90
89
  const result = await this.requestObject(cmd, storageItem, StorageItem.parse, 'POST');
91
90
  const savedItem = result.getData();
92
91
  if (savedItem === null) {
93
- console.log('Storage set item wrong data', { result, parsed: savedItem });
94
92
  throw new Error('Unable to save storage item: wrong response from API');
95
93
  }
96
94
  return savedItem;
@@ -109,7 +107,6 @@ export class StorageEndpoint extends Endpoint {
109
107
  const result = await this.requestObject(cmd, null, parser, 'DELETE');
110
108
  const x = result.getData();
111
109
  if (x === null) {
112
- console.log('Storage delete item wrong data', { result, parsed: x });
113
110
  throw new Error('Unable to delete storage item: wrong response from API');
114
111
  }
115
112
  return x.deleted;
@@ -16,7 +16,6 @@ export class UserEndpoint extends Endpoint {
16
16
  const result = await this.requestObject('/users/auth/providers', { provider: providerId }, parser, 'DELETE');
17
17
  const re = result.getData();
18
18
  if (re === null) {
19
- console.log('remove auth provider wrong data', { result, parsed: re });
20
19
  throw new Error('Unable to remove auth provider: wrong response from API');
21
20
  }
22
21
  return re.deleted;
@@ -59,9 +58,7 @@ export class UserEndpoint extends Endpoint {
59
58
  async create(user, password, invitationCode = null) {
60
59
  try {
61
60
  const url = '/users/';
62
- const data = user;
63
- data.password = password;
64
- data.invitation_code = invitationCode;
61
+ const data = { ...user, password, invitation_code: invitationCode };
65
62
  const result = await this.requestObject(url, data, User.parse, 'PUT', false);
66
63
  const createdUser = result.getData();
67
64
  if (createdUser === null) {
@@ -83,7 +80,6 @@ export class UserEndpoint extends Endpoint {
83
80
  const result = await this.requestObject(url, { email }, parser, 'POST', false);
84
81
  const re = result.getData();
85
82
  if (re === null) {
86
- console.log('reset password request wrong data', { result, parsed: re });
87
83
  throw new Error('Unable to request password reset: wrong response from API');
88
84
  }
89
85
  return re.requested;
@@ -102,7 +98,6 @@ export class UserEndpoint extends Endpoint {
102
98
  const result = await this.requestObject(url, { password }, parser, 'POST', false);
103
99
  const re = result.getData();
104
100
  if (re === null) {
105
- console.log('reset password wrong data', { result, parsed: re });
106
101
  throw new Error('Unable to reset password: wrong response from API');
107
102
  }
108
103
  return re.updated;
@@ -128,7 +123,6 @@ export class UserEndpoint extends Endpoint {
128
123
  const result = await this.requestObject(url, data, parser, 'POST', true);
129
124
  const re = result.getData();
130
125
  if (re === null) {
131
- console.log('created web push wrong data', { result, parsed: re });
132
126
  throw new Error('Unable to request web push: wrong response from API');
133
127
  }
134
128
  return re.created;
package/dist/index.d.ts CHANGED
@@ -7,7 +7,10 @@ export { QueryString } from './Http/Data/QueryString.js';
7
7
  export { HttpClient } from './Http/HttpClient.js';
8
8
  export { HttpClientRequest } from './Http/HttpClientRequest.js';
9
9
  export { HttpClientResponse } from './Http/HttpClientResponse.js';
10
- export { OAuthClient } from './Http/OAuthClient.js';
10
+ export { ITransport } from './Http/Transport/ITransport.js';
11
+ export { DirectTransport } from './Http/Transport/DirectTransport.js';
12
+ export type { DirectTransportRoute } from './Http/Transport/DirectTransport.js';
13
+ export { OAuthClient } from './Http/Transport/OAuthClient.js';
11
14
  export { OAuthClientOptions } from './Http/OAuthClientOptions.js';
12
15
  export { OAuthClientToken } from './Http/OAuthClientToken.js';
13
16
  export { ClientError } from './Http/ClientError.js';
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@ export { QueryString } from './Http/Data/QueryString.js';
2
2
  export { HttpClient } from './Http/HttpClient.js';
3
3
  export { HttpClientRequest } from './Http/HttpClientRequest.js';
4
4
  export { HttpClientResponse } from './Http/HttpClientResponse.js';
5
- export { OAuthClient } from './Http/OAuthClient.js';
5
+ export { DirectTransport } from './Http/Transport/DirectTransport.js';
6
+ export { OAuthClient } from './Http/Transport/OAuthClient.js';
6
7
  export { OAuthClientOptions } from './Http/OAuthClientOptions.js';
7
8
  export { OAuthClientToken } from './Http/OAuthClientToken.js';
8
9
  export { ClientError } from './Http/ClientError.js';
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.47.2";
1
+ export declare const VERSION = "1.48.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "1.47.2";
1
+ export const VERSION = "1.48.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attlaz-client",
3
- "version": "1.47.3",
3
+ "version": "1.48.1",
4
4
  "description": "Javascript Client to access Attlaz API",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",