@espressif/rainmaker-admin-sdk 1.3.0 → 1.4.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/CHANGELOG.md +56 -0
- package/dist/cjs/entries/ESPRMAuth.cjs +1 -0
- package/dist/cjs/entries/ESPRMBase.cjs +1 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/methods/ESPRMAuth/LoginWithAuthorizationCode.cjs +54 -0
- package/dist/cjs/services/ESPRMAPIManager.cjs +29 -4
- package/dist/cjs/utils/constants.cjs +4 -0
- package/dist/cjs/utils/error/errorMessages.cjs +3 -0
- package/dist/cjs/utils/error/parser.cjs +3 -2
- package/dist/esm/entries/ESPRMAuth.js +1 -0
- package/dist/esm/entries/ESPRMBase.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/methods/ESPRMAuth/LoginWithAuthorizationCode.js +52 -0
- package/dist/esm/services/ESPRMAPIManager.js +29 -4
- package/dist/esm/utils/constants.js +4 -0
- package/dist/esm/utils/error/errorMessages.js +3 -0
- package/dist/esm/utils/error/parser.js +3 -2
- package/dist/types/methods/ESPRMAdminOTAJob/GetJob.d.ts +12 -3
- package/dist/types/methods/ESPRMAuth/LoginWithAuthorizationCode.d.ts +40 -0
- package/dist/types/methods/ESPRMAuth/index.d.ts +1 -0
- package/dist/types/services/ESPRMAPIManager.d.ts +5 -0
- package/dist/types/types/auth.d.ts +30 -0
- package/dist/types/types/config.d.ts +5 -0
- package/dist/types/types/input.d.ts +1 -1
- package/dist/types/types/ota.d.ts +42 -8
- package/dist/types/utils/constants.d.ts +4 -0
- package/dist/types/utils/error/errorMessages.d.ts +3 -0
- package/dist/types/utils/error/parser.d.ts +4 -0
- package/dist/types-cjs/methods/ESPRMAdminOTAJob/GetJob.d.cts +12 -3
- package/dist/types-cjs/methods/ESPRMAuth/LoginWithAuthorizationCode.d.cts +40 -0
- package/dist/types-cjs/methods/ESPRMAuth/index.d.cts +1 -0
- package/dist/types-cjs/services/ESPRMAPIManager.d.cts +5 -0
- package/dist/types-cjs/types/auth.d.cts +30 -0
- package/dist/types-cjs/types/config.d.cts +5 -0
- package/dist/types-cjs/types/input.d.cts +1 -1
- package/dist/types-cjs/types/ota.d.cts +42 -8
- package/dist/types-cjs/utils/constants.d.cts +4 -0
- package/dist/types-cjs/utils/error/errorMessages.d.cts +3 -0
- package/dist/types-cjs/utils/error/parser.d.cts +4 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,62 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and follows the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format.
|
|
5
5
|
|
|
6
|
+
## [1.4.0] - 2026-10-07
|
|
7
|
+
|
|
8
|
+
Minor release that adds OAuth 2.0 authorization-code login, so dashboards can
|
|
9
|
+
complete social sign-in that uses `response_type=code` (WeChat on China
|
|
10
|
+
deployments) through the SDK instead of calling `/token` themselves. Existing
|
|
11
|
+
methods keep their signatures; the two behaviour changes below are fixes.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `ESPRMAuth.loginWithAuthorizationCode({ code, redirectUri, clientId })`
|
|
16
|
+
completes an OAuth 2.0 authorization-code login (`response_type=code`, used
|
|
17
|
+
by WeChat sign-in on China deployments). It sends a form-encoded
|
|
18
|
+
`POST {baseUrl}/token` and stores the returned tokens like `login()`. When
|
|
19
|
+
no refresh token is issued, an empty one is stored.
|
|
20
|
+
- `ESPRMRequestConfig.formData` sends a request body as
|
|
21
|
+
`application/x-www-form-urlencoded`.
|
|
22
|
+
- `ESPRMAPIManager.getRootUrl()` returns the configured `baseUrl` without the
|
|
23
|
+
API version segment.
|
|
24
|
+
- Types `AuthorizationCodeLoginParams`, `AuthorizationCodeTokenRequest` and
|
|
25
|
+
`OAuthTokenResponse`, and validation codes `MISSING_AUTHORIZATION_CODE`,
|
|
26
|
+
`MISSING_REDIRECT_URI` and `MISSING_OAUTH_CLIENT_ID`.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- `ESPAPIError` now reads RFC 6749 error bodies too. `errorCode` falls back to
|
|
31
|
+
`error`, and `description` falls back to `error_description`, when the
|
|
32
|
+
RainMaker `error_code` / `description` fields are absent.
|
|
33
|
+
- A trailing slash on `baseUrl` is now removed before the version segment is
|
|
34
|
+
appended. Before, it produced a double slash such as `https://host//v1`.
|
|
35
|
+
|
|
36
|
+
## [1.3.1] - 2026-10-07
|
|
37
|
+
|
|
38
|
+
Patch release that corrects the OTA job list types in `ESPRMAdminOTAJob.getJob`
|
|
39
|
+
to match what the backend accepts and returns. No runtime behaviour changes:
|
|
40
|
+
the SDK already forwarded every query parameter and returned the backend's
|
|
41
|
+
response as-is. Only the types were wrong.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- `GetOTAJobParams` now declares the filters the backend accepts:
|
|
46
|
+
`ota_job_name` (case-sensitive "contains" search), `ota_image_id`,
|
|
47
|
+
`archived` and `all`. Callers no longer need a cast to search.
|
|
48
|
+
- `GetOTAJobsResponse.otaJobs`, the key the backend actually returns the job
|
|
49
|
+
list under.
|
|
50
|
+
- `GetOTAJobByIdParams` and a `getJob({ ota_job_id })` overload that returns
|
|
51
|
+
`OTAJobInfo`. The backend answers an ID lookup with the job itself, not a
|
|
52
|
+
list.
|
|
53
|
+
|
|
54
|
+
### Deprecated
|
|
55
|
+
|
|
56
|
+
- `GetOTAJobsResponse.ota_update_jobs` and `GetOTAJobsResponse.total`. The
|
|
57
|
+
backend has never returned either; read `otaJobs` instead. Both will be
|
|
58
|
+
removed in the next minor release.
|
|
59
|
+
- `GetOTAJobParams.ota_job_id`. Pass `GetOTAJobByIdParams` to get the
|
|
60
|
+
correctly typed single-job response.
|
|
61
|
+
|
|
6
62
|
## [1.3.0] - 2026-10-06
|
|
7
63
|
|
|
8
64
|
Minor release that adds node deletion, node claiming through the standalone
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
require('../methods/ESPRMAuth/Login.cjs');
|
|
9
|
+
require('../methods/ESPRMAuth/LoginWithAuthorizationCode.cjs');
|
|
9
10
|
require('../methods/ESPRMAuth/Logout.cjs');
|
|
10
11
|
require('../methods/ESPRMAuth/ForgotPassword.cjs');
|
|
11
12
|
require('../methods/ESPRMAuth/ChangePassword.cjs');
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
require('../methods/ESPRMAuth/Login.cjs');
|
|
9
|
+
require('../methods/ESPRMAuth/LoginWithAuthorizationCode.cjs');
|
|
9
10
|
require('../methods/ESPRMAuth/Logout.cjs');
|
|
10
11
|
require('../methods/ESPRMAuth/ForgotPassword.cjs');
|
|
11
12
|
require('../methods/ESPRMAuth/ChangePassword.cjs');
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -58,6 +58,7 @@ var ESPRMAdminMatter = require('./ESPRMAdminMatter.cjs');
|
|
|
58
58
|
var ESPRMAdminNodeParams = require('./ESPRMAdminNodeParams.cjs');
|
|
59
59
|
var ESPRMClaim = require('./ESPRMClaim.cjs');
|
|
60
60
|
require('./methods/ESPRMAuth/Login.cjs');
|
|
61
|
+
require('./methods/ESPRMAuth/LoginWithAuthorizationCode.cjs');
|
|
61
62
|
require('./methods/ESPRMAuth/Logout.cjs');
|
|
62
63
|
require('./methods/ESPRMAuth/ForgotPassword.cjs');
|
|
63
64
|
require('./methods/ESPRMAuth/ChangePassword.cjs');
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
var ESPRMAuth = require('../../ESPRMAuth.cjs');
|
|
9
|
+
var ESPRMUser = require('../../ESPRMUser.cjs');
|
|
10
|
+
var ESPRMAPIManager = require('../../services/ESPRMAPIManager.cjs');
|
|
11
|
+
var constants = require('../../utils/constants.cjs');
|
|
12
|
+
var ESPValidationError = require('../../utils/error/ESPValidationError.cjs');
|
|
13
|
+
var parser = require('../../utils/error/parser.cjs');
|
|
14
|
+
|
|
15
|
+
function validateParams({ code, redirectUri, clientId, }) {
|
|
16
|
+
if (!code) {
|
|
17
|
+
throw new ESPValidationError.ESPValidationError(constants.ValidationErrorCodes.MISSING_AUTHORIZATION_CODE);
|
|
18
|
+
}
|
|
19
|
+
if (!redirectUri) {
|
|
20
|
+
throw new ESPValidationError.ESPValidationError(constants.ValidationErrorCodes.MISSING_REDIRECT_URI);
|
|
21
|
+
}
|
|
22
|
+
if (!clientId) {
|
|
23
|
+
throw new ESPValidationError.ESPValidationError(constants.ValidationErrorCodes.MISSING_OAUTH_CLIENT_ID);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
ESPRMAuth.ESPRMAuth.prototype.loginWithAuthorizationCode = async function (params) {
|
|
27
|
+
validateParams(params);
|
|
28
|
+
const tokenRequest = {
|
|
29
|
+
grant_type: "authorization_code",
|
|
30
|
+
code: params.code,
|
|
31
|
+
redirect_uri: params.redirectUri,
|
|
32
|
+
client_id: params.clientId,
|
|
33
|
+
};
|
|
34
|
+
const requestConfig = {
|
|
35
|
+
baseURL: ESPRMAPIManager.ESPRMAPIManager.getRootUrl(),
|
|
36
|
+
url: constants.APIEndpoints.OAUTH_TOKEN,
|
|
37
|
+
method: constants.HTTPMethods.POST,
|
|
38
|
+
formData: tokenRequest,
|
|
39
|
+
};
|
|
40
|
+
const responseData = await ESPRMAPIManager.ESPRMAPIManager.request(requestConfig);
|
|
41
|
+
if (!responseData?.access_token || !responseData?.id_token) {
|
|
42
|
+
throw parser.parseAPIErrorResponse({
|
|
43
|
+
status: "failure",
|
|
44
|
+
description: "Token response is missing access_token or id_token",
|
|
45
|
+
}, 200);
|
|
46
|
+
}
|
|
47
|
+
const userTokens = {
|
|
48
|
+
accessToken: responseData.access_token,
|
|
49
|
+
idToken: responseData.id_token,
|
|
50
|
+
refreshToken: responseData.refresh_token ?? "",
|
|
51
|
+
};
|
|
52
|
+
await ESPRMUser.ESPRMUser.storeTokens(userTokens);
|
|
53
|
+
return new ESPRMUser.ESPRMUser(userTokens);
|
|
54
|
+
};
|
|
@@ -20,11 +20,13 @@ var common = require('../types/common.cjs');
|
|
|
20
20
|
class ESPRMAPIManager {
|
|
21
21
|
static #instance;
|
|
22
22
|
#baseUrl;
|
|
23
|
+
#rootUrl;
|
|
23
24
|
#timeoutMs;
|
|
24
25
|
#claimingBaseUrl;
|
|
25
26
|
constructor(config) {
|
|
26
27
|
const { baseUrl, version, timeoutMs, claimingBaseUrl } = config;
|
|
27
|
-
this.#
|
|
28
|
+
this.#rootUrl = baseUrl.replace(/\/+$/, "");
|
|
29
|
+
this.#baseUrl = `${this.#rootUrl}/${version}`;
|
|
28
30
|
this.#timeoutMs = timeoutMs ?? constants.DEFAULT_REQUEST_TIMEOUT_MS;
|
|
29
31
|
this.#claimingBaseUrl = claimingBaseUrl?.replace(/\/+$/, "");
|
|
30
32
|
}
|
|
@@ -37,6 +39,13 @@ class ESPRMAPIManager {
|
|
|
37
39
|
}
|
|
38
40
|
return ESPRMAPIManager.#instance;
|
|
39
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Configured `baseUrl` without the API version segment, for the endpoints
|
|
44
|
+
* served outside `/{version}`, such as the OAuth `/token` endpoint.
|
|
45
|
+
*/
|
|
46
|
+
static getRootUrl() {
|
|
47
|
+
return ESPRMAPIManager.#getInstance().#rootUrl;
|
|
48
|
+
}
|
|
40
49
|
/**
|
|
41
50
|
* Base URL of the claiming service set through `claimingBaseUrl`.
|
|
42
51
|
*
|
|
@@ -82,6 +91,21 @@ class ESPRMAPIManager {
|
|
|
82
91
|
throw error;
|
|
83
92
|
}
|
|
84
93
|
}
|
|
94
|
+
static #serializeBody(requestConfig) {
|
|
95
|
+
if (requestConfig.formData) {
|
|
96
|
+
return {
|
|
97
|
+
contentType: "application/x-www-form-urlencoded",
|
|
98
|
+
body: new URLSearchParams(requestConfig.formData).toString(),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (requestConfig.data) {
|
|
102
|
+
return {
|
|
103
|
+
contentType: "application/json",
|
|
104
|
+
body: JSON.stringify(requestConfig.data),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
85
109
|
/**
|
|
86
110
|
* Send an unauthenticated request. Non-2xx responses and network/timeout
|
|
87
111
|
* failures reject with an {@link ESPAPIError}.
|
|
@@ -102,18 +126,19 @@ class ESPRMAPIManager {
|
|
|
102
126
|
requestUrl += `?${new URLSearchParams(queryEntries).toString()}`;
|
|
103
127
|
}
|
|
104
128
|
}
|
|
129
|
+
const { contentType, body } = ESPRMAPIManager.#serializeBody(requestConfig);
|
|
105
130
|
const fetchOptions = {
|
|
106
131
|
method: requestConfig.method,
|
|
107
132
|
// The API never redirects. Following one would re-send the body (login
|
|
108
133
|
// password, refresh token) to whatever host the redirect names.
|
|
109
134
|
redirect: "error",
|
|
110
135
|
headers: {
|
|
111
|
-
...(
|
|
136
|
+
...(contentType ? { "Content-Type": contentType } : {}),
|
|
112
137
|
...requestConfig.headers,
|
|
113
138
|
},
|
|
114
139
|
};
|
|
115
|
-
if (
|
|
116
|
-
fetchOptions.body =
|
|
140
|
+
if (body !== undefined) {
|
|
141
|
+
fetchOptions.body = body;
|
|
117
142
|
}
|
|
118
143
|
const timeoutMs = requestConfig.timeoutMs ?? instance.#timeoutMs;
|
|
119
144
|
let timer;
|
|
@@ -17,6 +17,7 @@ const APIEndpoints = {
|
|
|
17
17
|
LOGIN: "login2",
|
|
18
18
|
PASSWORD: "password2",
|
|
19
19
|
LOGOUT: "logout2",
|
|
20
|
+
OAUTH_TOKEN: "token",
|
|
20
21
|
ADMIN_USER: "admin/user2",
|
|
21
22
|
ADMIN_USER_SUMMARY: "admin/user2/summary",
|
|
22
23
|
ADMIN_NODES: "admin/nodes",
|
|
@@ -115,6 +116,9 @@ const ValidationErrorCodes = {
|
|
|
115
116
|
MISSING_LOGIN_PASSWORD: "MISSING_LOGIN_PASSWORD",
|
|
116
117
|
MISSING_USERNAME: "MISSING_USERNAME",
|
|
117
118
|
MISSING_VERIFICATION_CODE: "MISSING_VERIFICATION_CODE",
|
|
119
|
+
MISSING_AUTHORIZATION_CODE: "MISSING_AUTHORIZATION_CODE",
|
|
120
|
+
MISSING_REDIRECT_URI: "MISSING_REDIRECT_URI",
|
|
121
|
+
MISSING_OAUTH_CLIENT_ID: "MISSING_OAUTH_CLIENT_ID",
|
|
118
122
|
};
|
|
119
123
|
const StorageAdapterErrorCodes = {
|
|
120
124
|
UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: "UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API",
|
|
@@ -16,6 +16,9 @@ const validationErrorMessages = {
|
|
|
16
16
|
MISSING_LOGIN_PASSWORD: "Validation Error: Password is required.",
|
|
17
17
|
MISSING_USERNAME: "Validation Error: Username is required.",
|
|
18
18
|
MISSING_VERIFICATION_CODE: "Validation Error: Verification code is required.",
|
|
19
|
+
MISSING_AUTHORIZATION_CODE: "Validation Error: Authorization code is required.",
|
|
20
|
+
MISSING_REDIRECT_URI: "Validation Error: Redirect URI is required.",
|
|
21
|
+
MISSING_OAUTH_CLIENT_ID: "Validation Error: OAuth client ID is required.",
|
|
19
22
|
};
|
|
20
23
|
const storageAdapterErrorMessages = {
|
|
21
24
|
UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: "ESPStorageAdapterError: It seems like your environment doesn't support window.localstorage, you can define your own storage adapter while configuring the ESPRMBase instance. Please refer docs for more information.",
|
|
@@ -11,8 +11,9 @@ const parseAPIErrorResponse = (error, statusCode) => {
|
|
|
11
11
|
let description = "An error has occurred";
|
|
12
12
|
if (error) {
|
|
13
13
|
status = error.status ?? "";
|
|
14
|
-
errorCode = error.error_code ?? "";
|
|
15
|
-
description =
|
|
14
|
+
errorCode = error.error_code ?? error.error ?? "";
|
|
15
|
+
description =
|
|
16
|
+
error.description ?? error.error_description ?? "An error has occurred";
|
|
16
17
|
}
|
|
17
18
|
else {
|
|
18
19
|
throw error;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import '../methods/ESPRMAuth/Login.js';
|
|
7
|
+
import '../methods/ESPRMAuth/LoginWithAuthorizationCode.js';
|
|
7
8
|
import '../methods/ESPRMAuth/Logout.js';
|
|
8
9
|
import '../methods/ESPRMAuth/ForgotPassword.js';
|
|
9
10
|
import '../methods/ESPRMAuth/ChangePassword.js';
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import '../methods/ESPRMAuth/Login.js';
|
|
7
|
+
import '../methods/ESPRMAuth/LoginWithAuthorizationCode.js';
|
|
7
8
|
import '../methods/ESPRMAuth/Logout.js';
|
|
8
9
|
import '../methods/ESPRMAuth/ForgotPassword.js';
|
|
9
10
|
import '../methods/ESPRMAuth/ChangePassword.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -56,6 +56,7 @@ export { ESPRMAdminMatter } from './ESPRMAdminMatter.js';
|
|
|
56
56
|
export { ESPRMAdminNodeParams } from './ESPRMAdminNodeParams.js';
|
|
57
57
|
export { ESPRMClaim } from './ESPRMClaim.js';
|
|
58
58
|
import './methods/ESPRMAuth/Login.js';
|
|
59
|
+
import './methods/ESPRMAuth/LoginWithAuthorizationCode.js';
|
|
59
60
|
import './methods/ESPRMAuth/Logout.js';
|
|
60
61
|
import './methods/ESPRMAuth/ForgotPassword.js';
|
|
61
62
|
import './methods/ESPRMAuth/ChangePassword.js';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { ESPRMAuth } from '../../ESPRMAuth.js';
|
|
7
|
+
import { ESPRMUser } from '../../ESPRMUser.js';
|
|
8
|
+
import { ESPRMAPIManager } from '../../services/ESPRMAPIManager.js';
|
|
9
|
+
import { HTTPMethods, APIEndpoints, ValidationErrorCodes } from '../../utils/constants.js';
|
|
10
|
+
import { ESPValidationError } from '../../utils/error/ESPValidationError.js';
|
|
11
|
+
import { parseAPIErrorResponse } from '../../utils/error/parser.js';
|
|
12
|
+
|
|
13
|
+
function validateParams({ code, redirectUri, clientId, }) {
|
|
14
|
+
if (!code) {
|
|
15
|
+
throw new ESPValidationError(ValidationErrorCodes.MISSING_AUTHORIZATION_CODE);
|
|
16
|
+
}
|
|
17
|
+
if (!redirectUri) {
|
|
18
|
+
throw new ESPValidationError(ValidationErrorCodes.MISSING_REDIRECT_URI);
|
|
19
|
+
}
|
|
20
|
+
if (!clientId) {
|
|
21
|
+
throw new ESPValidationError(ValidationErrorCodes.MISSING_OAUTH_CLIENT_ID);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
ESPRMAuth.prototype.loginWithAuthorizationCode = async function (params) {
|
|
25
|
+
validateParams(params);
|
|
26
|
+
const tokenRequest = {
|
|
27
|
+
grant_type: "authorization_code",
|
|
28
|
+
code: params.code,
|
|
29
|
+
redirect_uri: params.redirectUri,
|
|
30
|
+
client_id: params.clientId,
|
|
31
|
+
};
|
|
32
|
+
const requestConfig = {
|
|
33
|
+
baseURL: ESPRMAPIManager.getRootUrl(),
|
|
34
|
+
url: APIEndpoints.OAUTH_TOKEN,
|
|
35
|
+
method: HTTPMethods.POST,
|
|
36
|
+
formData: tokenRequest,
|
|
37
|
+
};
|
|
38
|
+
const responseData = await ESPRMAPIManager.request(requestConfig);
|
|
39
|
+
if (!responseData?.access_token || !responseData?.id_token) {
|
|
40
|
+
throw parseAPIErrorResponse({
|
|
41
|
+
status: "failure",
|
|
42
|
+
description: "Token response is missing access_token or id_token",
|
|
43
|
+
}, 200);
|
|
44
|
+
}
|
|
45
|
+
const userTokens = {
|
|
46
|
+
accessToken: responseData.access_token,
|
|
47
|
+
idToken: responseData.id_token,
|
|
48
|
+
refreshToken: responseData.refresh_token ?? "",
|
|
49
|
+
};
|
|
50
|
+
await ESPRMUser.storeTokens(userTokens);
|
|
51
|
+
return new ESPRMUser(userTokens);
|
|
52
|
+
};
|
|
@@ -18,11 +18,13 @@ import { PropertyCheckMode } from '../types/common.js';
|
|
|
18
18
|
class ESPRMAPIManager {
|
|
19
19
|
static #instance;
|
|
20
20
|
#baseUrl;
|
|
21
|
+
#rootUrl;
|
|
21
22
|
#timeoutMs;
|
|
22
23
|
#claimingBaseUrl;
|
|
23
24
|
constructor(config) {
|
|
24
25
|
const { baseUrl, version, timeoutMs, claimingBaseUrl } = config;
|
|
25
|
-
this.#
|
|
26
|
+
this.#rootUrl = baseUrl.replace(/\/+$/, "");
|
|
27
|
+
this.#baseUrl = `${this.#rootUrl}/${version}`;
|
|
26
28
|
this.#timeoutMs = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
27
29
|
this.#claimingBaseUrl = claimingBaseUrl?.replace(/\/+$/, "");
|
|
28
30
|
}
|
|
@@ -35,6 +37,13 @@ class ESPRMAPIManager {
|
|
|
35
37
|
}
|
|
36
38
|
return ESPRMAPIManager.#instance;
|
|
37
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Configured `baseUrl` without the API version segment, for the endpoints
|
|
42
|
+
* served outside `/{version}`, such as the OAuth `/token` endpoint.
|
|
43
|
+
*/
|
|
44
|
+
static getRootUrl() {
|
|
45
|
+
return ESPRMAPIManager.#getInstance().#rootUrl;
|
|
46
|
+
}
|
|
38
47
|
/**
|
|
39
48
|
* Base URL of the claiming service set through `claimingBaseUrl`.
|
|
40
49
|
*
|
|
@@ -80,6 +89,21 @@ class ESPRMAPIManager {
|
|
|
80
89
|
throw error;
|
|
81
90
|
}
|
|
82
91
|
}
|
|
92
|
+
static #serializeBody(requestConfig) {
|
|
93
|
+
if (requestConfig.formData) {
|
|
94
|
+
return {
|
|
95
|
+
contentType: "application/x-www-form-urlencoded",
|
|
96
|
+
body: new URLSearchParams(requestConfig.formData).toString(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (requestConfig.data) {
|
|
100
|
+
return {
|
|
101
|
+
contentType: "application/json",
|
|
102
|
+
body: JSON.stringify(requestConfig.data),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
83
107
|
/**
|
|
84
108
|
* Send an unauthenticated request. Non-2xx responses and network/timeout
|
|
85
109
|
* failures reject with an {@link ESPAPIError}.
|
|
@@ -100,18 +124,19 @@ class ESPRMAPIManager {
|
|
|
100
124
|
requestUrl += `?${new URLSearchParams(queryEntries).toString()}`;
|
|
101
125
|
}
|
|
102
126
|
}
|
|
127
|
+
const { contentType, body } = ESPRMAPIManager.#serializeBody(requestConfig);
|
|
103
128
|
const fetchOptions = {
|
|
104
129
|
method: requestConfig.method,
|
|
105
130
|
// The API never redirects. Following one would re-send the body (login
|
|
106
131
|
// password, refresh token) to whatever host the redirect names.
|
|
107
132
|
redirect: "error",
|
|
108
133
|
headers: {
|
|
109
|
-
...(
|
|
134
|
+
...(contentType ? { "Content-Type": contentType } : {}),
|
|
110
135
|
...requestConfig.headers,
|
|
111
136
|
},
|
|
112
137
|
};
|
|
113
|
-
if (
|
|
114
|
-
fetchOptions.body =
|
|
138
|
+
if (body !== undefined) {
|
|
139
|
+
fetchOptions.body = body;
|
|
115
140
|
}
|
|
116
141
|
const timeoutMs = requestConfig.timeoutMs ?? instance.#timeoutMs;
|
|
117
142
|
let timer;
|
|
@@ -15,6 +15,7 @@ const APIEndpoints = {
|
|
|
15
15
|
LOGIN: "login2",
|
|
16
16
|
PASSWORD: "password2",
|
|
17
17
|
LOGOUT: "logout2",
|
|
18
|
+
OAUTH_TOKEN: "token",
|
|
18
19
|
ADMIN_USER: "admin/user2",
|
|
19
20
|
ADMIN_USER_SUMMARY: "admin/user2/summary",
|
|
20
21
|
ADMIN_NODES: "admin/nodes",
|
|
@@ -113,6 +114,9 @@ const ValidationErrorCodes = {
|
|
|
113
114
|
MISSING_LOGIN_PASSWORD: "MISSING_LOGIN_PASSWORD",
|
|
114
115
|
MISSING_USERNAME: "MISSING_USERNAME",
|
|
115
116
|
MISSING_VERIFICATION_CODE: "MISSING_VERIFICATION_CODE",
|
|
117
|
+
MISSING_AUTHORIZATION_CODE: "MISSING_AUTHORIZATION_CODE",
|
|
118
|
+
MISSING_REDIRECT_URI: "MISSING_REDIRECT_URI",
|
|
119
|
+
MISSING_OAUTH_CLIENT_ID: "MISSING_OAUTH_CLIENT_ID",
|
|
116
120
|
};
|
|
117
121
|
const StorageAdapterErrorCodes = {
|
|
118
122
|
UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: "UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API",
|
|
@@ -14,6 +14,9 @@ const validationErrorMessages = {
|
|
|
14
14
|
MISSING_LOGIN_PASSWORD: "Validation Error: Password is required.",
|
|
15
15
|
MISSING_USERNAME: "Validation Error: Username is required.",
|
|
16
16
|
MISSING_VERIFICATION_CODE: "Validation Error: Verification code is required.",
|
|
17
|
+
MISSING_AUTHORIZATION_CODE: "Validation Error: Authorization code is required.",
|
|
18
|
+
MISSING_REDIRECT_URI: "Validation Error: Redirect URI is required.",
|
|
19
|
+
MISSING_OAUTH_CLIENT_ID: "Validation Error: OAuth client ID is required.",
|
|
17
20
|
};
|
|
18
21
|
const storageAdapterErrorMessages = {
|
|
19
22
|
UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: "ESPStorageAdapterError: It seems like your environment doesn't support window.localstorage, you can define your own storage adapter while configuring the ESPRMBase instance. Please refer docs for more information.",
|
|
@@ -9,8 +9,9 @@ const parseAPIErrorResponse = (error, statusCode) => {
|
|
|
9
9
|
let description = "An error has occurred";
|
|
10
10
|
if (error) {
|
|
11
11
|
status = error.status ?? "";
|
|
12
|
-
errorCode = error.error_code ?? "";
|
|
13
|
-
description =
|
|
12
|
+
errorCode = error.error_code ?? error.error ?? "";
|
|
13
|
+
description =
|
|
14
|
+
error.description ?? error.error_description ?? "An error has occurred";
|
|
14
15
|
}
|
|
15
16
|
else {
|
|
16
17
|
throw error;
|
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
*
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { GetOTAJobParams } from "../../types/ota.js";
|
|
7
|
-
import { GetOTAJobsResponse } from "../../types/ota.js";
|
|
6
|
+
import { GetOTAJobByIdParams, GetOTAJobParams, GetOTAJobsResponse, OTAJobInfo } from "../../types/ota.js";
|
|
8
7
|
declare module "../../ESPRMAdminOTAJob.js" {
|
|
9
8
|
interface ESPRMAdminOTAJob {
|
|
10
9
|
/**
|
|
11
|
-
* Retrieves
|
|
10
|
+
* Retrieves a single OTA job by ID.
|
|
11
|
+
*
|
|
12
|
+
* @param params - The ID of the job to fetch.
|
|
13
|
+
* @returns The OTA job. The backend returns the job itself, not a list.
|
|
14
|
+
*/
|
|
15
|
+
getJob(params: GetOTAJobByIdParams): Promise<OTAJobInfo>;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves a page of OTA jobs, optionally filtered by name or image.
|
|
18
|
+
*
|
|
19
|
+
* When nothing matches, the backend rejects with HTTP 404 and error code
|
|
20
|
+
* `105012` rather than returning an empty list.
|
|
12
21
|
*
|
|
13
22
|
* @param params - Optional query parameters to filter the job list.
|
|
14
23
|
* @returns The list of OTA jobs matching the specified criteria.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { ESPRMUser } from "../../ESPRMUser.js";
|
|
7
|
+
import { AuthorizationCodeLoginParams } from "../../types/auth.js";
|
|
8
|
+
declare module "../../ESPRMAuth.js" {
|
|
9
|
+
interface ESPRMAuth {
|
|
10
|
+
/**
|
|
11
|
+
* Complete an OAuth 2.0 authorization-code login (`response_type=code`)
|
|
12
|
+
* by exchanging the code returned on the redirect URI for tokens.
|
|
13
|
+
*
|
|
14
|
+
* Sends `POST {baseUrl}/token` (outside the API version path) as
|
|
15
|
+
* `application/x-www-form-urlencoded`, without an `Authorization` header.
|
|
16
|
+
* Stores the returned tokens like {@link ESPRMAuth.login}. When the server
|
|
17
|
+
* issues no refresh token, an empty one is stored, so the session ends
|
|
18
|
+
* when the access token expires.
|
|
19
|
+
*
|
|
20
|
+
* @param params - Authorization code plus the `redirect_uri` and
|
|
21
|
+
* `client_id` sent in the authorize request
|
|
22
|
+
* @returns A new {@link ESPRMUser} instance with active session tokens
|
|
23
|
+
* @throws ESPValidationError when `code`, `redirectUri` or `clientId` is empty
|
|
24
|
+
* @throws ESPAPIError on a non-2xx response, where `errorCode` carries the
|
|
25
|
+
* RFC 6749 `error` value (for example `invalid_grant`), or when a 2xx
|
|
26
|
+
* response lacks `access_token` or `id_token`
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const code = new URLSearchParams(window.location.search).get("code");
|
|
31
|
+
* const user = await ESPRMBase.getAuthInstance().loginWithAuthorizationCode({
|
|
32
|
+
* code,
|
|
33
|
+
* redirectUri: "https://dashboard.example.com/socialauth",
|
|
34
|
+
* clientId: "<OAUTH_CLIENT_ID>",
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
loginWithAuthorizationCode(params: AuthorizationCodeLoginParams): Promise<ESPRMUser>;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -13,6 +13,11 @@ export declare class ESPRMAPIManager {
|
|
|
13
13
|
#private;
|
|
14
14
|
private constructor();
|
|
15
15
|
static initialize(config: ESPRMAPIManagerConfig): void;
|
|
16
|
+
/**
|
|
17
|
+
* Configured `baseUrl` without the API version segment, for the endpoints
|
|
18
|
+
* served outside `/{version}`, such as the OAuth `/token` endpoint.
|
|
19
|
+
*/
|
|
20
|
+
static getRootUrl(): string;
|
|
16
21
|
/**
|
|
17
22
|
* Base URL of the claiming service set through `claimingBaseUrl`.
|
|
18
23
|
*
|
|
@@ -84,3 +84,33 @@ export interface GetUserInfoResponse {
|
|
|
84
84
|
locale?: string;
|
|
85
85
|
tags?: string[];
|
|
86
86
|
}
|
|
87
|
+
/** Parameters for {@link ESPRMAuth.loginWithAuthorizationCode}. */
|
|
88
|
+
export interface AuthorizationCodeLoginParams {
|
|
89
|
+
/** Authorization code from the `code` query parameter of the OAuth callback. */
|
|
90
|
+
code: string;
|
|
91
|
+
/** Redirect URI sent in the authorize request. Must match it exactly. */
|
|
92
|
+
redirectUri: string;
|
|
93
|
+
/** OAuth client ID sent in the authorize request. */
|
|
94
|
+
clientId: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Form body for `POST /token` with `grant_type=authorization_code`.
|
|
98
|
+
* A type alias, not an interface, so it is assignable to the
|
|
99
|
+
* `Record<string, string>` that `formData` takes.
|
|
100
|
+
*/
|
|
101
|
+
export type AuthorizationCodeTokenRequest = {
|
|
102
|
+
grant_type: "authorization_code";
|
|
103
|
+
code: string;
|
|
104
|
+
redirect_uri: string;
|
|
105
|
+
client_id: string;
|
|
106
|
+
};
|
|
107
|
+
/** Successful `POST /token` response (RFC 6749 section 5.1). */
|
|
108
|
+
export interface OAuthTokenResponse {
|
|
109
|
+
access_token: string;
|
|
110
|
+
id_token: string;
|
|
111
|
+
/** Omitted or empty when the grant issues no refresh token. */
|
|
112
|
+
refresh_token?: string;
|
|
113
|
+
token_type?: string;
|
|
114
|
+
/** Access-token lifetime in seconds. */
|
|
115
|
+
expires_in?: number;
|
|
116
|
+
}
|
|
@@ -45,6 +45,11 @@ export interface ESPRMRequestConfig {
|
|
|
45
45
|
method: string;
|
|
46
46
|
/** Request body data (will be JSON-stringified). */
|
|
47
47
|
data?: Record<string, unknown> | object;
|
|
48
|
+
/**
|
|
49
|
+
* Request body sent as `application/x-www-form-urlencoded`, for the OAuth
|
|
50
|
+
* endpoints. Takes precedence over `data` when both are set.
|
|
51
|
+
*/
|
|
52
|
+
formData?: Record<string, string>;
|
|
48
53
|
/** URL query parameters. */
|
|
49
54
|
params?: Record<string, string> | object;
|
|
50
55
|
/** Additional HTTP headers. */
|
|
@@ -8,7 +8,7 @@ export type { ESPRMBaseConfig, ESPRMAPIManagerConfig, UserTokensData } from "./c
|
|
|
8
8
|
export type { SignUpRequest, ConfirmUserRequest, ChangePasswordRequest, ForgotPasswordRequest, ForgotPasswordConfirmRequest, LogoutRequest, UpdateUserProfileRequest, DeleteAccountParams, } from "./auth.js";
|
|
9
9
|
export type { CreateAdminUserRequest, UpdateAdminUserRequest, GetAdminUsersParams, DeleteAdminUserParams, } from "./admin_user.js";
|
|
10
10
|
export type { GetAdminNodesParams, ActivateDeactivateNodesParams, GetNodeTagsParams, NodeAttachTagsRequest, } from "./node.js";
|
|
11
|
-
export type { CreateOTAImageRequest, GetOTAImagesParams, ArchiveOTAImageParams, DeleteOTAImageParams, GetOTAImageUploadUrlParams, ConfirmOTAImageUploadRequest, GetOTAPackageUploadUrlParams, ConfirmOTAPackageUploadRequest, CreateOTAJobRequest, CreateOTAJobQueryOptions, OtaJobDownloadWindow, OtaJobValidity, OtaJobMetadata, OtaSecureBoot, OtaNetworkSerialisation, GetOTAJobParams, UpdateOTAJobParams, CancelOTAJobRequest, ArchiveOTAJobRequest, GetOTAJobStatusParams, GetOTAJobStatusSummaryParams, RetriggerOTAJobRequest, } from "./ota.js";
|
|
11
|
+
export type { CreateOTAImageRequest, GetOTAImagesParams, ArchiveOTAImageParams, DeleteOTAImageParams, GetOTAImageUploadUrlParams, ConfirmOTAImageUploadRequest, GetOTAPackageUploadUrlParams, ConfirmOTAPackageUploadRequest, CreateOTAJobRequest, CreateOTAJobQueryOptions, OtaJobDownloadWindow, OtaJobValidity, OtaJobMetadata, OtaSecureBoot, OtaNetworkSerialisation, GetOTAJobParams, GetOTAJobByIdParams, UpdateOTAJobParams, CancelOTAJobRequest, ArchiveOTAJobRequest, GetOTAJobStatusParams, GetOTAJobStatusSummaryParams, RetriggerOTAJobRequest, } from "./ota.js";
|
|
12
12
|
export type { CreateAdminGroupRequest, UpdateAdminGroupRequest, GetAdminGroupsParams, } from "./group.js";
|
|
13
13
|
export type { GetAdminTagsParams, AttachDetachTagsParams, GetTagNamesParams, } from "./tag.js";
|
|
14
14
|
export type { GetCommandRequestsParams, AddCommandRequestBody, CancelCommandRequestsParams, CommandRequestStatus, } from "./command_response.js";
|
|
@@ -159,14 +159,40 @@ export interface CreateOTAJobQueryOptions {
|
|
|
159
159
|
/** Serialise OTA delivery per local network. */
|
|
160
160
|
network_serialised?: boolean;
|
|
161
161
|
}
|
|
162
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Parameters for listing or searching OTA jobs.
|
|
164
|
+
*
|
|
165
|
+
* Without a filter the backend returns a page of jobs, scoped by `archived`
|
|
166
|
+
* and `all`. `ota_job_name` and `ota_image_id` switch it to a search.
|
|
167
|
+
*/
|
|
163
168
|
export interface GetOTAJobParams {
|
|
164
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Filter by specific OTA job ID.
|
|
171
|
+
*
|
|
172
|
+
* @deprecated The backend answers an ID lookup with a single job, not a
|
|
173
|
+
* list. Pass {@link GetOTAJobByIdParams} so `getJob` returns `OTAJobInfo`.
|
|
174
|
+
*/
|
|
165
175
|
ota_job_id?: string;
|
|
166
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* Return jobs whose name contains this text. Case-sensitive. Covers archived
|
|
178
|
+
* and unarchived jobs alike: `archived` and `all` are ignored.
|
|
179
|
+
*/
|
|
180
|
+
ota_job_name?: string;
|
|
181
|
+
/** Return every job created from this OTA image. Honours `archived`. */
|
|
182
|
+
ota_image_id?: string;
|
|
183
|
+
/** Maximum number of records to return (backend default and maximum: 25). */
|
|
167
184
|
num_records?: string;
|
|
168
|
-
/** Job ID to start pagination from. */
|
|
185
|
+
/** Job ID to start pagination from (the previous page's `next_id`). */
|
|
169
186
|
start_id?: string;
|
|
187
|
+
/** List archived jobs instead of unarchived ones. Defaults to `false`. */
|
|
188
|
+
archived?: boolean;
|
|
189
|
+
/** List archived and unarchived jobs together. Defaults to `false`. */
|
|
190
|
+
all?: boolean;
|
|
191
|
+
}
|
|
192
|
+
/** Parameters for fetching a single OTA job by ID. */
|
|
193
|
+
export interface GetOTAJobByIdParams {
|
|
194
|
+
/** ID of the OTA job to fetch. */
|
|
195
|
+
ota_job_id: string;
|
|
170
196
|
}
|
|
171
197
|
/** Parameters for updating an OTA job. */
|
|
172
198
|
export interface UpdateOTAJobParams {
|
|
@@ -368,11 +394,19 @@ export interface OTAJobRetriggerResponse {
|
|
|
368
394
|
}
|
|
369
395
|
/** Paginated response containing a list of OTA jobs. */
|
|
370
396
|
export interface GetOTAJobsResponse {
|
|
371
|
-
/**
|
|
372
|
-
|
|
373
|
-
/** ID to use for fetching the next page. */
|
|
397
|
+
/** OTA jobs on this page. */
|
|
398
|
+
otaJobs?: OTAJobInfo[];
|
|
399
|
+
/** ID to use for fetching the next page. Absent on the last page. */
|
|
374
400
|
next_id?: string;
|
|
375
|
-
/**
|
|
401
|
+
/**
|
|
402
|
+
* @deprecated Never returned by the backend; read `otaJobs` instead.
|
|
403
|
+
* Will be removed in the next minor release.
|
|
404
|
+
*/
|
|
405
|
+
ota_update_jobs?: OTAJobInfo[];
|
|
406
|
+
/**
|
|
407
|
+
* @deprecated Never returned by the backend. Will be removed in the next
|
|
408
|
+
* minor release.
|
|
409
|
+
*/
|
|
376
410
|
total?: number;
|
|
377
411
|
}
|
|
378
412
|
/** Per-node OTA status row (GET admin/otajob/status). */
|
|
@@ -15,6 +15,7 @@ declare const APIEndpoints: {
|
|
|
15
15
|
readonly LOGIN: "login2";
|
|
16
16
|
readonly PASSWORD: "password2";
|
|
17
17
|
readonly LOGOUT: "logout2";
|
|
18
|
+
readonly OAUTH_TOKEN: "token";
|
|
18
19
|
readonly ADMIN_USER: "admin/user2";
|
|
19
20
|
readonly ADMIN_USER_SUMMARY: "admin/user2/summary";
|
|
20
21
|
readonly ADMIN_NODES: "admin/nodes";
|
|
@@ -113,6 +114,9 @@ declare const ValidationErrorCodes: {
|
|
|
113
114
|
readonly MISSING_LOGIN_PASSWORD: "MISSING_LOGIN_PASSWORD";
|
|
114
115
|
readonly MISSING_USERNAME: "MISSING_USERNAME";
|
|
115
116
|
readonly MISSING_VERIFICATION_CODE: "MISSING_VERIFICATION_CODE";
|
|
117
|
+
readonly MISSING_AUTHORIZATION_CODE: "MISSING_AUTHORIZATION_CODE";
|
|
118
|
+
readonly MISSING_REDIRECT_URI: "MISSING_REDIRECT_URI";
|
|
119
|
+
readonly MISSING_OAUTH_CLIENT_ID: "MISSING_OAUTH_CLIENT_ID";
|
|
116
120
|
};
|
|
117
121
|
declare const StorageAdapterErrorCodes: {
|
|
118
122
|
readonly UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: "UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API";
|
|
@@ -14,6 +14,9 @@ declare const validationErrorMessages: {
|
|
|
14
14
|
MISSING_LOGIN_PASSWORD: string;
|
|
15
15
|
MISSING_USERNAME: string;
|
|
16
16
|
MISSING_VERIFICATION_CODE: string;
|
|
17
|
+
MISSING_AUTHORIZATION_CODE: string;
|
|
18
|
+
MISSING_REDIRECT_URI: string;
|
|
19
|
+
MISSING_OAUTH_CLIENT_ID: string;
|
|
17
20
|
};
|
|
18
21
|
declare const storageAdapterErrorMessages: {
|
|
19
22
|
UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: string;
|
|
@@ -8,6 +8,10 @@ interface RawAPIErrorBody {
|
|
|
8
8
|
status?: string;
|
|
9
9
|
error_code?: string;
|
|
10
10
|
description?: string;
|
|
11
|
+
/** RFC 6749 error code, returned by the OAuth endpoints instead of `error_code`. */
|
|
12
|
+
error?: string;
|
|
13
|
+
/** RFC 6749 error text, returned by the OAuth endpoints instead of `description`. */
|
|
14
|
+
error_description?: string;
|
|
11
15
|
}
|
|
12
16
|
declare const parseAPIErrorResponse: (error: RawAPIErrorBody, statusCode: number) => ESPAPIError;
|
|
13
17
|
export { parseAPIErrorResponse };
|
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
*
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { GetOTAJobParams } from "../../types/ota.cjs";
|
|
7
|
-
import { GetOTAJobsResponse } from "../../types/ota.cjs";
|
|
6
|
+
import { GetOTAJobByIdParams, GetOTAJobParams, GetOTAJobsResponse, OTAJobInfo } from "../../types/ota.cjs";
|
|
8
7
|
declare module "../../ESPRMAdminOTAJob.cjs" {
|
|
9
8
|
interface ESPRMAdminOTAJob {
|
|
10
9
|
/**
|
|
11
|
-
* Retrieves
|
|
10
|
+
* Retrieves a single OTA job by ID.
|
|
11
|
+
*
|
|
12
|
+
* @param params - The ID of the job to fetch.
|
|
13
|
+
* @returns The OTA job. The backend returns the job itself, not a list.
|
|
14
|
+
*/
|
|
15
|
+
getJob(params: GetOTAJobByIdParams): Promise<OTAJobInfo>;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves a page of OTA jobs, optionally filtered by name or image.
|
|
18
|
+
*
|
|
19
|
+
* When nothing matches, the backend rejects with HTTP 404 and error code
|
|
20
|
+
* `105012` rather than returning an empty list.
|
|
12
21
|
*
|
|
13
22
|
* @param params - Optional query parameters to filter the job list.
|
|
14
23
|
* @returns The list of OTA jobs matching the specified criteria.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
3
|
+
*
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { ESPRMUser } from "../../ESPRMUser.cjs";
|
|
7
|
+
import { AuthorizationCodeLoginParams } from "../../types/auth.cjs";
|
|
8
|
+
declare module "../../ESPRMAuth.cjs" {
|
|
9
|
+
interface ESPRMAuth {
|
|
10
|
+
/**
|
|
11
|
+
* Complete an OAuth 2.0 authorization-code login (`response_type=code`)
|
|
12
|
+
* by exchanging the code returned on the redirect URI for tokens.
|
|
13
|
+
*
|
|
14
|
+
* Sends `POST {baseUrl}/token` (outside the API version path) as
|
|
15
|
+
* `application/x-www-form-urlencoded`, without an `Authorization` header.
|
|
16
|
+
* Stores the returned tokens like {@link ESPRMAuth.login}. When the server
|
|
17
|
+
* issues no refresh token, an empty one is stored, so the session ends
|
|
18
|
+
* when the access token expires.
|
|
19
|
+
*
|
|
20
|
+
* @param params - Authorization code plus the `redirect_uri` and
|
|
21
|
+
* `client_id` sent in the authorize request
|
|
22
|
+
* @returns A new {@link ESPRMUser} instance with active session tokens
|
|
23
|
+
* @throws ESPValidationError when `code`, `redirectUri` or `clientId` is empty
|
|
24
|
+
* @throws ESPAPIError on a non-2xx response, where `errorCode` carries the
|
|
25
|
+
* RFC 6749 `error` value (for example `invalid_grant`), or when a 2xx
|
|
26
|
+
* response lacks `access_token` or `id_token`
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const code = new URLSearchParams(window.location.search).get("code");
|
|
31
|
+
* const user = await ESPRMBase.getAuthInstance().loginWithAuthorizationCode({
|
|
32
|
+
* code,
|
|
33
|
+
* redirectUri: "https://dashboard.example.com/socialauth",
|
|
34
|
+
* clientId: "<OAUTH_CLIENT_ID>",
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
loginWithAuthorizationCode(params: AuthorizationCodeLoginParams): Promise<ESPRMUser>;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -13,6 +13,11 @@ export declare class ESPRMAPIManager {
|
|
|
13
13
|
#private;
|
|
14
14
|
private constructor();
|
|
15
15
|
static initialize(config: ESPRMAPIManagerConfig): void;
|
|
16
|
+
/**
|
|
17
|
+
* Configured `baseUrl` without the API version segment, for the endpoints
|
|
18
|
+
* served outside `/{version}`, such as the OAuth `/token` endpoint.
|
|
19
|
+
*/
|
|
20
|
+
static getRootUrl(): string;
|
|
16
21
|
/**
|
|
17
22
|
* Base URL of the claiming service set through `claimingBaseUrl`.
|
|
18
23
|
*
|
|
@@ -84,3 +84,33 @@ export interface GetUserInfoResponse {
|
|
|
84
84
|
locale?: string;
|
|
85
85
|
tags?: string[];
|
|
86
86
|
}
|
|
87
|
+
/** Parameters for {@link ESPRMAuth.loginWithAuthorizationCode}. */
|
|
88
|
+
export interface AuthorizationCodeLoginParams {
|
|
89
|
+
/** Authorization code from the `code` query parameter of the OAuth callback. */
|
|
90
|
+
code: string;
|
|
91
|
+
/** Redirect URI sent in the authorize request. Must match it exactly. */
|
|
92
|
+
redirectUri: string;
|
|
93
|
+
/** OAuth client ID sent in the authorize request. */
|
|
94
|
+
clientId: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Form body for `POST /token` with `grant_type=authorization_code`.
|
|
98
|
+
* A type alias, not an interface, so it is assignable to the
|
|
99
|
+
* `Record<string, string>` that `formData` takes.
|
|
100
|
+
*/
|
|
101
|
+
export type AuthorizationCodeTokenRequest = {
|
|
102
|
+
grant_type: "authorization_code";
|
|
103
|
+
code: string;
|
|
104
|
+
redirect_uri: string;
|
|
105
|
+
client_id: string;
|
|
106
|
+
};
|
|
107
|
+
/** Successful `POST /token` response (RFC 6749 section 5.1). */
|
|
108
|
+
export interface OAuthTokenResponse {
|
|
109
|
+
access_token: string;
|
|
110
|
+
id_token: string;
|
|
111
|
+
/** Omitted or empty when the grant issues no refresh token. */
|
|
112
|
+
refresh_token?: string;
|
|
113
|
+
token_type?: string;
|
|
114
|
+
/** Access-token lifetime in seconds. */
|
|
115
|
+
expires_in?: number;
|
|
116
|
+
}
|
|
@@ -45,6 +45,11 @@ export interface ESPRMRequestConfig {
|
|
|
45
45
|
method: string;
|
|
46
46
|
/** Request body data (will be JSON-stringified). */
|
|
47
47
|
data?: Record<string, unknown> | object;
|
|
48
|
+
/**
|
|
49
|
+
* Request body sent as `application/x-www-form-urlencoded`, for the OAuth
|
|
50
|
+
* endpoints. Takes precedence over `data` when both are set.
|
|
51
|
+
*/
|
|
52
|
+
formData?: Record<string, string>;
|
|
48
53
|
/** URL query parameters. */
|
|
49
54
|
params?: Record<string, string> | object;
|
|
50
55
|
/** Additional HTTP headers. */
|
|
@@ -8,7 +8,7 @@ export type { ESPRMBaseConfig, ESPRMAPIManagerConfig, UserTokensData } from "./c
|
|
|
8
8
|
export type { SignUpRequest, ConfirmUserRequest, ChangePasswordRequest, ForgotPasswordRequest, ForgotPasswordConfirmRequest, LogoutRequest, UpdateUserProfileRequest, DeleteAccountParams, } from "./auth.cjs";
|
|
9
9
|
export type { CreateAdminUserRequest, UpdateAdminUserRequest, GetAdminUsersParams, DeleteAdminUserParams, } from "./admin_user.cjs";
|
|
10
10
|
export type { GetAdminNodesParams, ActivateDeactivateNodesParams, GetNodeTagsParams, NodeAttachTagsRequest, } from "./node.cjs";
|
|
11
|
-
export type { CreateOTAImageRequest, GetOTAImagesParams, ArchiveOTAImageParams, DeleteOTAImageParams, GetOTAImageUploadUrlParams, ConfirmOTAImageUploadRequest, GetOTAPackageUploadUrlParams, ConfirmOTAPackageUploadRequest, CreateOTAJobRequest, CreateOTAJobQueryOptions, OtaJobDownloadWindow, OtaJobValidity, OtaJobMetadata, OtaSecureBoot, OtaNetworkSerialisation, GetOTAJobParams, UpdateOTAJobParams, CancelOTAJobRequest, ArchiveOTAJobRequest, GetOTAJobStatusParams, GetOTAJobStatusSummaryParams, RetriggerOTAJobRequest, } from "./ota.cjs";
|
|
11
|
+
export type { CreateOTAImageRequest, GetOTAImagesParams, ArchiveOTAImageParams, DeleteOTAImageParams, GetOTAImageUploadUrlParams, ConfirmOTAImageUploadRequest, GetOTAPackageUploadUrlParams, ConfirmOTAPackageUploadRequest, CreateOTAJobRequest, CreateOTAJobQueryOptions, OtaJobDownloadWindow, OtaJobValidity, OtaJobMetadata, OtaSecureBoot, OtaNetworkSerialisation, GetOTAJobParams, GetOTAJobByIdParams, UpdateOTAJobParams, CancelOTAJobRequest, ArchiveOTAJobRequest, GetOTAJobStatusParams, GetOTAJobStatusSummaryParams, RetriggerOTAJobRequest, } from "./ota.cjs";
|
|
12
12
|
export type { CreateAdminGroupRequest, UpdateAdminGroupRequest, GetAdminGroupsParams, } from "./group.cjs";
|
|
13
13
|
export type { GetAdminTagsParams, AttachDetachTagsParams, GetTagNamesParams, } from "./tag.cjs";
|
|
14
14
|
export type { GetCommandRequestsParams, AddCommandRequestBody, CancelCommandRequestsParams, CommandRequestStatus, } from "./command_response.cjs";
|
|
@@ -159,14 +159,40 @@ export interface CreateOTAJobQueryOptions {
|
|
|
159
159
|
/** Serialise OTA delivery per local network. */
|
|
160
160
|
network_serialised?: boolean;
|
|
161
161
|
}
|
|
162
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Parameters for listing or searching OTA jobs.
|
|
164
|
+
*
|
|
165
|
+
* Without a filter the backend returns a page of jobs, scoped by `archived`
|
|
166
|
+
* and `all`. `ota_job_name` and `ota_image_id` switch it to a search.
|
|
167
|
+
*/
|
|
163
168
|
export interface GetOTAJobParams {
|
|
164
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Filter by specific OTA job ID.
|
|
171
|
+
*
|
|
172
|
+
* @deprecated The backend answers an ID lookup with a single job, not a
|
|
173
|
+
* list. Pass {@link GetOTAJobByIdParams} so `getJob` returns `OTAJobInfo`.
|
|
174
|
+
*/
|
|
165
175
|
ota_job_id?: string;
|
|
166
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* Return jobs whose name contains this text. Case-sensitive. Covers archived
|
|
178
|
+
* and unarchived jobs alike: `archived` and `all` are ignored.
|
|
179
|
+
*/
|
|
180
|
+
ota_job_name?: string;
|
|
181
|
+
/** Return every job created from this OTA image. Honours `archived`. */
|
|
182
|
+
ota_image_id?: string;
|
|
183
|
+
/** Maximum number of records to return (backend default and maximum: 25). */
|
|
167
184
|
num_records?: string;
|
|
168
|
-
/** Job ID to start pagination from. */
|
|
185
|
+
/** Job ID to start pagination from (the previous page's `next_id`). */
|
|
169
186
|
start_id?: string;
|
|
187
|
+
/** List archived jobs instead of unarchived ones. Defaults to `false`. */
|
|
188
|
+
archived?: boolean;
|
|
189
|
+
/** List archived and unarchived jobs together. Defaults to `false`. */
|
|
190
|
+
all?: boolean;
|
|
191
|
+
}
|
|
192
|
+
/** Parameters for fetching a single OTA job by ID. */
|
|
193
|
+
export interface GetOTAJobByIdParams {
|
|
194
|
+
/** ID of the OTA job to fetch. */
|
|
195
|
+
ota_job_id: string;
|
|
170
196
|
}
|
|
171
197
|
/** Parameters for updating an OTA job. */
|
|
172
198
|
export interface UpdateOTAJobParams {
|
|
@@ -368,11 +394,19 @@ export interface OTAJobRetriggerResponse {
|
|
|
368
394
|
}
|
|
369
395
|
/** Paginated response containing a list of OTA jobs. */
|
|
370
396
|
export interface GetOTAJobsResponse {
|
|
371
|
-
/**
|
|
372
|
-
|
|
373
|
-
/** ID to use for fetching the next page. */
|
|
397
|
+
/** OTA jobs on this page. */
|
|
398
|
+
otaJobs?: OTAJobInfo[];
|
|
399
|
+
/** ID to use for fetching the next page. Absent on the last page. */
|
|
374
400
|
next_id?: string;
|
|
375
|
-
/**
|
|
401
|
+
/**
|
|
402
|
+
* @deprecated Never returned by the backend; read `otaJobs` instead.
|
|
403
|
+
* Will be removed in the next minor release.
|
|
404
|
+
*/
|
|
405
|
+
ota_update_jobs?: OTAJobInfo[];
|
|
406
|
+
/**
|
|
407
|
+
* @deprecated Never returned by the backend. Will be removed in the next
|
|
408
|
+
* minor release.
|
|
409
|
+
*/
|
|
376
410
|
total?: number;
|
|
377
411
|
}
|
|
378
412
|
/** Per-node OTA status row (GET admin/otajob/status). */
|
|
@@ -15,6 +15,7 @@ declare const APIEndpoints: {
|
|
|
15
15
|
readonly LOGIN: "login2";
|
|
16
16
|
readonly PASSWORD: "password2";
|
|
17
17
|
readonly LOGOUT: "logout2";
|
|
18
|
+
readonly OAUTH_TOKEN: "token";
|
|
18
19
|
readonly ADMIN_USER: "admin/user2";
|
|
19
20
|
readonly ADMIN_USER_SUMMARY: "admin/user2/summary";
|
|
20
21
|
readonly ADMIN_NODES: "admin/nodes";
|
|
@@ -113,6 +114,9 @@ declare const ValidationErrorCodes: {
|
|
|
113
114
|
readonly MISSING_LOGIN_PASSWORD: "MISSING_LOGIN_PASSWORD";
|
|
114
115
|
readonly MISSING_USERNAME: "MISSING_USERNAME";
|
|
115
116
|
readonly MISSING_VERIFICATION_CODE: "MISSING_VERIFICATION_CODE";
|
|
117
|
+
readonly MISSING_AUTHORIZATION_CODE: "MISSING_AUTHORIZATION_CODE";
|
|
118
|
+
readonly MISSING_REDIRECT_URI: "MISSING_REDIRECT_URI";
|
|
119
|
+
readonly MISSING_OAUTH_CLIENT_ID: "MISSING_OAUTH_CLIENT_ID";
|
|
116
120
|
};
|
|
117
121
|
declare const StorageAdapterErrorCodes: {
|
|
118
122
|
readonly UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: "UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API";
|
|
@@ -14,6 +14,9 @@ declare const validationErrorMessages: {
|
|
|
14
14
|
MISSING_LOGIN_PASSWORD: string;
|
|
15
15
|
MISSING_USERNAME: string;
|
|
16
16
|
MISSING_VERIFICATION_CODE: string;
|
|
17
|
+
MISSING_AUTHORIZATION_CODE: string;
|
|
18
|
+
MISSING_REDIRECT_URI: string;
|
|
19
|
+
MISSING_OAUTH_CLIENT_ID: string;
|
|
17
20
|
};
|
|
18
21
|
declare const storageAdapterErrorMessages: {
|
|
19
22
|
UNSUPPORTED_DEFAULT_STORAGE_ADAPTER_API: string;
|
|
@@ -8,6 +8,10 @@ interface RawAPIErrorBody {
|
|
|
8
8
|
status?: string;
|
|
9
9
|
error_code?: string;
|
|
10
10
|
description?: string;
|
|
11
|
+
/** RFC 6749 error code, returned by the OAuth endpoints instead of `error_code`. */
|
|
12
|
+
error?: string;
|
|
13
|
+
/** RFC 6749 error text, returned by the OAuth endpoints instead of `description`. */
|
|
14
|
+
error_description?: string;
|
|
11
15
|
}
|
|
12
16
|
declare const parseAPIErrorResponse: (error: RawAPIErrorBody, statusCode: number) => ESPAPIError;
|
|
13
17
|
export { parseAPIErrorResponse };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@espressif/rainmaker-admin-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Espressif's Rainmaker Admin SDK enables seamless integration of admin applications with the ESP Rainmaker ecosystem.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Espressif Systems (Shanghai) CO LTD",
|