@rockcarver/frodo-lib 0.11.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/.eslintrc +32 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/README.md +121 -0
- package/.github/workflows/pipeline.yml +287 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +512 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/LICENSE +21 -0
- package/README.md +8 -0
- package/docs/CONTRIBUTE.md +96 -0
- package/docs/PIPELINE.md +169 -0
- package/docs/images/npm_versioning_guidelines.png +0 -0
- package/docs/images/release_pipeline.png +0 -0
- package/jsconfig.json +6 -0
- package/package.json +95 -0
- package/resources/sampleEntitiesFile.json +8 -0
- package/resources/sampleEnvFile.env +2 -0
- package/src/api/AuthenticateApi.js +33 -0
- package/src/api/BaseApi.js +242 -0
- package/src/api/CirclesOfTrustApi.js +87 -0
- package/src/api/EmailTemplateApi.js +37 -0
- package/src/api/IdmConfigApi.js +88 -0
- package/src/api/LogApi.js +45 -0
- package/src/api/ManagedObjectApi.js +62 -0
- package/src/api/OAuth2ClientApi.js +69 -0
- package/src/api/OAuth2OIDCApi.js +73 -0
- package/src/api/OAuth2ProviderApi.js +32 -0
- package/src/api/RealmApi.js +99 -0
- package/src/api/Saml2Api.js +176 -0
- package/src/api/ScriptApi.js +84 -0
- package/src/api/SecretsApi.js +151 -0
- package/src/api/ServerInfoApi.js +41 -0
- package/src/api/SocialIdentityProvidersApi.js +114 -0
- package/src/api/StartupApi.js +45 -0
- package/src/api/ThemeApi.js +181 -0
- package/src/api/TreeApi.js +207 -0
- package/src/api/VariablesApi.js +104 -0
- package/src/api/utils/ApiUtils.js +77 -0
- package/src/api/utils/ApiUtils.test.js +96 -0
- package/src/api/utils/Base64.js +62 -0
- package/src/index.js +32 -0
- package/src/index.test.js +13 -0
- package/src/ops/AdminOps.js +901 -0
- package/src/ops/AuthenticateOps.js +342 -0
- package/src/ops/CirclesOfTrustOps.js +350 -0
- package/src/ops/ConnectionProfileOps.js +254 -0
- package/src/ops/EmailTemplateOps.js +326 -0
- package/src/ops/IdmOps.js +227 -0
- package/src/ops/IdpOps.js +342 -0
- package/src/ops/JourneyOps.js +2026 -0
- package/src/ops/LogOps.js +357 -0
- package/src/ops/ManagedObjectOps.js +34 -0
- package/src/ops/OAuth2ClientOps.js +151 -0
- package/src/ops/OrganizationOps.js +85 -0
- package/src/ops/RealmOps.js +139 -0
- package/src/ops/SamlOps.js +541 -0
- package/src/ops/ScriptOps.js +211 -0
- package/src/ops/SecretsOps.js +288 -0
- package/src/ops/StartupOps.js +114 -0
- package/src/ops/ThemeOps.js +379 -0
- package/src/ops/VariablesOps.js +185 -0
- package/src/ops/templates/OAuth2ClientTemplate.json +270 -0
- package/src/ops/templates/OrgModelUserAttributesTemplate.json +149 -0
- package/src/ops/templates/cloud/GenericExtensionAttributesTemplate.json +392 -0
- package/src/ops/templates/cloud/managed.json +4119 -0
- package/src/ops/utils/Console.js +434 -0
- package/src/ops/utils/DataProtection.js +92 -0
- package/src/ops/utils/DataProtection.test.js +28 -0
- package/src/ops/utils/ExportImportUtils.js +146 -0
- package/src/ops/utils/ExportImportUtils.test.js +119 -0
- package/src/ops/utils/OpsUtils.js +76 -0
- package/src/ops/utils/Wordwrap.js +11 -0
- package/src/storage/SessionStorage.js +45 -0
- package/src/storage/StaticStorage.js +15 -0
- package/test/e2e/journey/baseline/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/baseline/Login.journey.json +205 -0
- package/test/e2e/journey/baseline/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/baseline/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/baseline/Registration.journey.json +249 -0
- package/test/e2e/journey/baseline/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/baseline/UpdatePassword.journey.json +323 -0
- package/test/e2e/journey/baseline/allAlphaJourneys.journeys.json +1520 -0
- package/test/e2e/journey/delete/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/delete/Login.journey.json +205 -0
- package/test/e2e/journey/delete/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/delete/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/delete/Registration.journey.json +249 -0
- package/test/e2e/journey/delete/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/delete/UpdatePassword.journey.json +323 -0
- package/test/e2e/journey/delete/deleteMe.journey.json +230 -0
- package/test/e2e/journey/list/Disabled.journey.json +43 -0
- package/test/e2e/journey/list/ForgottenUsername.journey.json +216 -0
- package/test/e2e/journey/list/Login.journey.json +205 -0
- package/test/e2e/journey/list/PasswordGrant.journey.json +139 -0
- package/test/e2e/journey/list/ProgressiveProfile.journey.json +198 -0
- package/test/e2e/journey/list/Registration.journey.json +249 -0
- package/test/e2e/journey/list/ResetPassword.journey.json +268 -0
- package/test/e2e/journey/list/UpdatePassword.journey.json +323 -0
- package/test/e2e/setup.js +107 -0
- package/test/e2e/theme/baseline/Contrast.theme.json +95 -0
- package/test/e2e/theme/baseline/Highlander.theme.json +95 -0
- package/test/e2e/theme/baseline/Robroy.theme.json +95 -0
- package/test/e2e/theme/baseline/Starter-Theme.theme.json +94 -0
- package/test/e2e/theme/baseline/Zardoz.theme.json +95 -0
- package/test/e2e/theme/import/Contrast.theme.json +95 -0
- package/test/e2e/theme/import/Highlander.theme.json +95 -0
- package/test/e2e/theme/import/Robroy.theme.json +95 -0
- package/test/e2e/theme/import/Starter-Theme.theme.json +94 -0
- package/test/e2e/theme/import/Zardoz.default.theme.json +95 -0
- package/test/fs_tmp/.gitkeep +2 -0
- package/test/global/setup.js +65 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { getConfigEntity, putConfigEntity } from './IdmConfigApi.js';
|
|
2
|
+
import { getCurrentRealmName } from './utils/ApiUtils.js';
|
|
3
|
+
|
|
4
|
+
const THEMEREALM_ID = 'ui/themerealm';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get realm themes
|
|
8
|
+
* @param {Object} themes object containing themes
|
|
9
|
+
* @returns {Object} array of theme pertaining to the current realm
|
|
10
|
+
*/
|
|
11
|
+
function getRealmThemes(themes) {
|
|
12
|
+
return themes.realm[getCurrentRealmName()]
|
|
13
|
+
? themes.realm[getCurrentRealmName()]
|
|
14
|
+
: [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get all themes
|
|
19
|
+
* @returns {Promise} a promise that resolves to an array of themes
|
|
20
|
+
*/
|
|
21
|
+
export async function getThemes() {
|
|
22
|
+
return getConfigEntity(THEMEREALM_ID).then((response) =>
|
|
23
|
+
getRealmThemes(response.data)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get theme by id
|
|
29
|
+
* @param {String} id theme id
|
|
30
|
+
* @returns {Promise} a promise that resolves to an array of themes
|
|
31
|
+
*/
|
|
32
|
+
export async function getTheme(id) {
|
|
33
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
34
|
+
return getRealmThemes(themes).filter((theme) => theme._id === id);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get theme by name
|
|
39
|
+
* @param {String} name theme name
|
|
40
|
+
* @returns {Promise} a promise that resolves to an array of themes
|
|
41
|
+
*/
|
|
42
|
+
export async function getThemeByName(name) {
|
|
43
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
44
|
+
return getRealmThemes(themes).filter((theme) => theme.name === name);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Put theme by id
|
|
49
|
+
* @param {String} id theme id
|
|
50
|
+
* @param {Object} data theme object
|
|
51
|
+
* @returns {Promise} a promise that resolves to an object containing a themes object
|
|
52
|
+
*/
|
|
53
|
+
export async function putTheme(id, data) {
|
|
54
|
+
const themeData = data;
|
|
55
|
+
themeData._id = id;
|
|
56
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
57
|
+
let isNew = true;
|
|
58
|
+
const realmThemes = getRealmThemes(themes).map((theme) => {
|
|
59
|
+
if (theme._id === id) {
|
|
60
|
+
isNew = false;
|
|
61
|
+
return themeData;
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line no-param-reassign
|
|
64
|
+
if (themeData.isDefault) theme.isDefault = false;
|
|
65
|
+
return theme;
|
|
66
|
+
});
|
|
67
|
+
if (isNew) {
|
|
68
|
+
realmThemes.push(themeData);
|
|
69
|
+
}
|
|
70
|
+
themes.realm[getCurrentRealmName()] = realmThemes;
|
|
71
|
+
return putConfigEntity(THEMEREALM_ID, themes);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Put theme by name
|
|
76
|
+
* @param {String} name theme name
|
|
77
|
+
* @param {Object} data theme object
|
|
78
|
+
* @returns {Promise} a promise that resolves to an object containing a themes object
|
|
79
|
+
*/
|
|
80
|
+
export async function putThemeByName(name, data) {
|
|
81
|
+
const themeData = data;
|
|
82
|
+
themeData.name = name;
|
|
83
|
+
const themes = await getConfigEntity(THEMEREALM_ID);
|
|
84
|
+
let isNew = true;
|
|
85
|
+
const realmThemes = getRealmThemes(themes).map((theme) => {
|
|
86
|
+
if (theme.name === name) {
|
|
87
|
+
isNew = false;
|
|
88
|
+
return themeData;
|
|
89
|
+
}
|
|
90
|
+
// eslint-disable-next-line no-param-reassign
|
|
91
|
+
if (themeData.isDefault) theme.isDefault = false;
|
|
92
|
+
return theme;
|
|
93
|
+
});
|
|
94
|
+
if (isNew) {
|
|
95
|
+
realmThemes.push(themeData);
|
|
96
|
+
}
|
|
97
|
+
themes.realm[getCurrentRealmName()] = realmThemes;
|
|
98
|
+
return putConfigEntity(THEMEREALM_ID, themes);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Put all themes
|
|
103
|
+
* @param {Object} data themes object containing all themes for all realms
|
|
104
|
+
* @returns {Promise} a promise that resolves to an object containing a themes object
|
|
105
|
+
*/
|
|
106
|
+
export async function putThemes(data) {
|
|
107
|
+
const allThemesData = data;
|
|
108
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
109
|
+
const allThemeIDs = Object.keys(allThemesData);
|
|
110
|
+
const existingThemeIDs = [];
|
|
111
|
+
let defaultThemeId = null;
|
|
112
|
+
// update existing themes
|
|
113
|
+
let realmThemes = getRealmThemes(themes).map((theme) => {
|
|
114
|
+
if (allThemesData[theme._id]) {
|
|
115
|
+
existingThemeIDs.push(theme._id);
|
|
116
|
+
// remember the id of the last default theme
|
|
117
|
+
if (allThemesData[theme._id].isDefault) defaultThemeId = theme._id;
|
|
118
|
+
return allThemesData[theme._id];
|
|
119
|
+
}
|
|
120
|
+
return theme;
|
|
121
|
+
});
|
|
122
|
+
const newThemeIDs = allThemeIDs.filter(
|
|
123
|
+
(id) => !existingThemeIDs.includes(id)
|
|
124
|
+
);
|
|
125
|
+
// add new themes
|
|
126
|
+
newThemeIDs.forEach((themeId) => {
|
|
127
|
+
// remember the id of the last default theme
|
|
128
|
+
if (allThemesData[themeId].isDefault) defaultThemeId = themeId;
|
|
129
|
+
realmThemes.push(allThemesData[themeId]);
|
|
130
|
+
});
|
|
131
|
+
// if we imported a default theme, flag all the other themes as not default
|
|
132
|
+
if (defaultThemeId) {
|
|
133
|
+
realmThemes = realmThemes.map((theme) => {
|
|
134
|
+
// eslint-disable-next-line no-param-reassign
|
|
135
|
+
theme.isDefault = theme._id === defaultThemeId;
|
|
136
|
+
return theme;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
themes.realm[getCurrentRealmName()] = realmThemes;
|
|
140
|
+
return putConfigEntity(THEMEREALM_ID, themes);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Delete theme by id
|
|
145
|
+
* @param {String} id theme id
|
|
146
|
+
* @returns {Promise} a promise that resolves to an object containing a themes object
|
|
147
|
+
*/
|
|
148
|
+
export async function deleteTheme(id) {
|
|
149
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
150
|
+
const realmThemes = getRealmThemes(themes);
|
|
151
|
+
const finalThemes = realmThemes.filter((theme) => theme._id !== id);
|
|
152
|
+
if (realmThemes.length === finalThemes.length)
|
|
153
|
+
throw new Error(`${id} not found`);
|
|
154
|
+
themes.realm[getCurrentRealmName()] = realmThemes;
|
|
155
|
+
return putConfigEntity(THEMEREALM_ID, themes);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Delete theme by name
|
|
160
|
+
* @param {String} name theme name
|
|
161
|
+
* @returns {Promise} a promise that resolves to an object containing a themes object
|
|
162
|
+
*/
|
|
163
|
+
export async function deleteThemeByName(name) {
|
|
164
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
165
|
+
const realmThemes = getRealmThemes(themes);
|
|
166
|
+
const finalThemes = realmThemes.filter((theme) => theme.name !== name);
|
|
167
|
+
if (realmThemes.length === finalThemes.length)
|
|
168
|
+
throw new Error(`${name} not found`);
|
|
169
|
+
themes.realm[getCurrentRealmName()] = finalThemes;
|
|
170
|
+
return putConfigEntity(THEMEREALM_ID, themes);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Delete all themes
|
|
175
|
+
* @returns {Promise} a promise that resolves to an array of themes
|
|
176
|
+
*/
|
|
177
|
+
export async function deleteThemes() {
|
|
178
|
+
const themes = (await getConfigEntity(THEMEREALM_ID)).data;
|
|
179
|
+
themes.realm[getCurrentRealmName()] = [];
|
|
180
|
+
return putConfigEntity(THEMEREALM_ID, themes);
|
|
181
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import util from 'util';
|
|
2
|
+
import { deleteDeepByKey, getCurrentRealmPath } from './utils/ApiUtils.js';
|
|
3
|
+
import { generateAmApi } from './BaseApi.js';
|
|
4
|
+
import storage from '../storage/SessionStorage.js';
|
|
5
|
+
|
|
6
|
+
const nodeURLTemplate =
|
|
7
|
+
'%s/json%s/realm-config/authentication/authenticationtrees/nodes/%s/%s';
|
|
8
|
+
const queryAllNodesByTypeURLTemplate =
|
|
9
|
+
'%s/json%s/realm-config/authentication/authenticationtrees/nodes/%s?_queryFilter=true';
|
|
10
|
+
const queryAllNodesURLTemplate =
|
|
11
|
+
'%s/json%s/realm-config/authentication/authenticationtrees/nodes?_action=nextdescendents';
|
|
12
|
+
const queryAllNodeTypesURLTemplate =
|
|
13
|
+
'%s/json%s/realm-config/authentication/authenticationtrees/nodes?_action=getAllTypes';
|
|
14
|
+
const treeByIdURLTemplate =
|
|
15
|
+
'%s/json%s/realm-config/authentication/authenticationtrees/trees/%s';
|
|
16
|
+
const queryAllTreesURLTemplate =
|
|
17
|
+
'%s/json%s/realm-config/authentication/authenticationtrees/trees?_queryFilter=true';
|
|
18
|
+
|
|
19
|
+
const apiVersion = 'protocol=2.1,resource=1.0';
|
|
20
|
+
const getTreeApiConfig = () => {
|
|
21
|
+
const configPath = getCurrentRealmPath();
|
|
22
|
+
return {
|
|
23
|
+
path: `${configPath}/authentication/authenticationtrees`,
|
|
24
|
+
apiVersion,
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export async function getNodeTypes() {
|
|
29
|
+
const urlString = util.format(
|
|
30
|
+
queryAllNodeTypesURLTemplate,
|
|
31
|
+
storage.session.getTenant(),
|
|
32
|
+
getCurrentRealmPath()
|
|
33
|
+
);
|
|
34
|
+
return generateAmApi(getTreeApiConfig()).post(
|
|
35
|
+
urlString,
|
|
36
|
+
{},
|
|
37
|
+
{
|
|
38
|
+
withCredentials: true,
|
|
39
|
+
headers: { 'Accept-Encoding': 'gzip, deflate, br' },
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get all nodes
|
|
46
|
+
* @returns {Promise} a promise that resolves to an object containing an array of all node objects
|
|
47
|
+
*/
|
|
48
|
+
export async function getNodes() {
|
|
49
|
+
const urlString = util.format(
|
|
50
|
+
queryAllNodesURLTemplate,
|
|
51
|
+
storage.session.getTenant(),
|
|
52
|
+
getCurrentRealmPath()
|
|
53
|
+
);
|
|
54
|
+
return generateAmApi(getTreeApiConfig()).post(
|
|
55
|
+
urlString,
|
|
56
|
+
{},
|
|
57
|
+
{
|
|
58
|
+
withCredentials: true,
|
|
59
|
+
headers: { 'Accept-Encoding': 'gzip, deflate, br' },
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get all nodes by type
|
|
66
|
+
* @param {*} type node type
|
|
67
|
+
* @returns {Promise} a promise that resolves to an object containing an array of node objects of the requested type
|
|
68
|
+
*/
|
|
69
|
+
export async function getNodesByType(type) {
|
|
70
|
+
const urlString = util.format(
|
|
71
|
+
queryAllNodesByTypeURLTemplate,
|
|
72
|
+
storage.session.getTenant(),
|
|
73
|
+
getCurrentRealmPath(),
|
|
74
|
+
type
|
|
75
|
+
);
|
|
76
|
+
return generateAmApi(getTreeApiConfig()).get(urlString, {
|
|
77
|
+
withCredentials: true,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get node by uuid and type
|
|
83
|
+
* @param {String} id node uuid
|
|
84
|
+
* @param {String} nodeType node type
|
|
85
|
+
* @returns {Promise} a promise that resolves to an object containing a node object
|
|
86
|
+
*/
|
|
87
|
+
export async function getNode(id, nodeType) {
|
|
88
|
+
const urlString = util.format(
|
|
89
|
+
nodeURLTemplate,
|
|
90
|
+
storage.session.getTenant(),
|
|
91
|
+
getCurrentRealmPath(storage.session.getRealm()),
|
|
92
|
+
nodeType,
|
|
93
|
+
id
|
|
94
|
+
);
|
|
95
|
+
return generateAmApi(getTreeApiConfig()).get(urlString, {
|
|
96
|
+
withCredentials: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Put node by uuid and type
|
|
102
|
+
* @param {String} id node uuid
|
|
103
|
+
* @param {String} nodeType node type
|
|
104
|
+
* @param {Object} data node object
|
|
105
|
+
* @returns {Promise} a promise that resolves to an object containing a node object
|
|
106
|
+
*/
|
|
107
|
+
export async function putNode(id, nodeType, nodeData) {
|
|
108
|
+
// until we figure out a way to use transport keys in Frodo,
|
|
109
|
+
// we'll have to drop those encrypted attributes.
|
|
110
|
+
const data = deleteDeepByKey(nodeData, '-encrypted');
|
|
111
|
+
const urlString = util.format(
|
|
112
|
+
nodeURLTemplate,
|
|
113
|
+
storage.session.getTenant(),
|
|
114
|
+
getCurrentRealmPath(storage.session.getRealm()),
|
|
115
|
+
nodeType,
|
|
116
|
+
id
|
|
117
|
+
);
|
|
118
|
+
return generateAmApi(getTreeApiConfig()).put(urlString, data, {
|
|
119
|
+
withCredentials: true,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Delete node by uuid and type
|
|
125
|
+
* @param {String} id node uuid
|
|
126
|
+
* @param {String} nodeType node type
|
|
127
|
+
* @returns {Promise} a promise that resolves to an object containing a node object
|
|
128
|
+
*/
|
|
129
|
+
export async function deleteNode(id, nodeType) {
|
|
130
|
+
const urlString = util.format(
|
|
131
|
+
nodeURLTemplate,
|
|
132
|
+
storage.session.getTenant(),
|
|
133
|
+
getCurrentRealmPath(),
|
|
134
|
+
nodeType,
|
|
135
|
+
id
|
|
136
|
+
);
|
|
137
|
+
return generateAmApi(getTreeApiConfig()).delete(urlString, {
|
|
138
|
+
withCredentials: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get all trees
|
|
144
|
+
* @returns {Promise} a promise that resolves to an object containing an array of tree objects
|
|
145
|
+
*/
|
|
146
|
+
export async function getTrees() {
|
|
147
|
+
const urlString = util.format(
|
|
148
|
+
queryAllTreesURLTemplate,
|
|
149
|
+
storage.session.getTenant(),
|
|
150
|
+
getCurrentRealmPath()
|
|
151
|
+
);
|
|
152
|
+
return generateAmApi(getTreeApiConfig()).get(urlString, {
|
|
153
|
+
withCredentials: true,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get tree by id/name
|
|
159
|
+
* @param {String} id tree id/name
|
|
160
|
+
* @returns {Promise} a promise that resolves to an object containing a tree object
|
|
161
|
+
*/
|
|
162
|
+
export async function getTree(id) {
|
|
163
|
+
const urlString = util.format(
|
|
164
|
+
treeByIdURLTemplate,
|
|
165
|
+
storage.session.getTenant(),
|
|
166
|
+
getCurrentRealmPath(),
|
|
167
|
+
id
|
|
168
|
+
);
|
|
169
|
+
return generateAmApi(getTreeApiConfig()).get(urlString, {
|
|
170
|
+
withCredentials: true,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Put tree by id/name
|
|
176
|
+
* @param {String} id tree id/name
|
|
177
|
+
* @param {Object} data tree object
|
|
178
|
+
* @returns {Promise} a promise that resolves to an object containing a tree object
|
|
179
|
+
*/
|
|
180
|
+
export async function putTree(id, data) {
|
|
181
|
+
const urlString = util.format(
|
|
182
|
+
treeByIdURLTemplate,
|
|
183
|
+
storage.session.getTenant(),
|
|
184
|
+
getCurrentRealmPath(storage.session.getRealm()),
|
|
185
|
+
id
|
|
186
|
+
);
|
|
187
|
+
return generateAmApi(getTreeApiConfig()).put(urlString, data, {
|
|
188
|
+
withCredentials: true,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Delete tree by id/name
|
|
194
|
+
* @param {String} id tree id/name
|
|
195
|
+
* @returns {Promise} a promise that resolves to an object containing a tree object
|
|
196
|
+
*/
|
|
197
|
+
export async function deleteTree(id) {
|
|
198
|
+
const urlString = util.format(
|
|
199
|
+
treeByIdURLTemplate,
|
|
200
|
+
storage.session.getTenant(),
|
|
201
|
+
getCurrentRealmPath(),
|
|
202
|
+
id
|
|
203
|
+
);
|
|
204
|
+
return generateAmApi(getTreeApiConfig()).delete(urlString, {
|
|
205
|
+
withCredentials: true,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import util from 'util';
|
|
2
|
+
import { encode } from './utils/Base64.js';
|
|
3
|
+
import { getTenantURL, getCurrentRealmPath } from './utils/ApiUtils.js';
|
|
4
|
+
import { generateESVApi } from './BaseApi.js';
|
|
5
|
+
import storage from '../storage/SessionStorage.js';
|
|
6
|
+
|
|
7
|
+
const variablesListURLTemplate = '%s/environment/variables';
|
|
8
|
+
const variableURLTemplate = '%s/environment/variables/%s';
|
|
9
|
+
const variableSetDescriptionURLTemplate = `${variableURLTemplate}?_action=setDescription`;
|
|
10
|
+
|
|
11
|
+
const apiVersion = 'protocol=1.0,resource=1.0';
|
|
12
|
+
const getApiConfig = () => {
|
|
13
|
+
const configPath = getCurrentRealmPath();
|
|
14
|
+
return {
|
|
15
|
+
path: `${configPath}/environment/secrets`,
|
|
16
|
+
apiVersion,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get all variables
|
|
22
|
+
* @returns {Promise} a promise that resolves to an object containing an array of variable objects
|
|
23
|
+
*/
|
|
24
|
+
export async function getVariables() {
|
|
25
|
+
const urlString = util.format(
|
|
26
|
+
variablesListURLTemplate,
|
|
27
|
+
getTenantURL(storage.session.getTenant())
|
|
28
|
+
);
|
|
29
|
+
return generateESVApi(getApiConfig()).get(urlString, {
|
|
30
|
+
withCredentials: true,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get variable by id/name
|
|
36
|
+
* @param {String} id variable id/name
|
|
37
|
+
* @returns {Promise} a promise that resolves to an object containing a variable object
|
|
38
|
+
*/
|
|
39
|
+
export async function getVariable(id) {
|
|
40
|
+
const urlString = util.format(
|
|
41
|
+
variableURLTemplate,
|
|
42
|
+
getTenantURL(storage.session.getTenant()),
|
|
43
|
+
id
|
|
44
|
+
);
|
|
45
|
+
return generateESVApi(getApiConfig()).get(urlString, {
|
|
46
|
+
withCredentials: true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Put variable by id/name
|
|
52
|
+
* @param {String} id variable id/name
|
|
53
|
+
* @param {String} value variable value
|
|
54
|
+
* @param {String} description variable description
|
|
55
|
+
* @returns {Promise} a promise that resolves to an object containing a variable object
|
|
56
|
+
*/
|
|
57
|
+
export async function putVariable(id, value, description) {
|
|
58
|
+
const data = {};
|
|
59
|
+
if (value) data.valueBase64 = encode(value);
|
|
60
|
+
if (description) data.description = description;
|
|
61
|
+
const urlString = util.format(
|
|
62
|
+
variableURLTemplate,
|
|
63
|
+
getTenantURL(storage.session.getTenant()),
|
|
64
|
+
id
|
|
65
|
+
);
|
|
66
|
+
return generateESVApi(getApiConfig()).put(urlString, data, {
|
|
67
|
+
withCredentials: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Set variable description
|
|
73
|
+
* @param {*} id variable id/name
|
|
74
|
+
* @param {*} description variable description
|
|
75
|
+
* @returns {Promise} a promise that resolves to an object containing a status object
|
|
76
|
+
*/
|
|
77
|
+
export async function setVariableDescription(id, description) {
|
|
78
|
+
const urlString = util.format(
|
|
79
|
+
variableSetDescriptionURLTemplate,
|
|
80
|
+
getTenantURL(storage.session.getTenant()),
|
|
81
|
+
id
|
|
82
|
+
);
|
|
83
|
+
return generateESVApi(getApiConfig()).post(
|
|
84
|
+
urlString,
|
|
85
|
+
{ description },
|
|
86
|
+
{ withCredentials: true }
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Delete variable by id/name
|
|
92
|
+
* @param {String} id variable id/name
|
|
93
|
+
* @returns {Promise} a promise that resolves to an object containing a variable object
|
|
94
|
+
*/
|
|
95
|
+
export async function deleteVariable(id) {
|
|
96
|
+
const urlString = util.format(
|
|
97
|
+
variableURLTemplate,
|
|
98
|
+
getTenantURL(storage.session.getTenant()),
|
|
99
|
+
id
|
|
100
|
+
);
|
|
101
|
+
return generateESVApi(getApiConfig()).delete(urlString, {
|
|
102
|
+
withCredentials: true,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import util from 'util';
|
|
2
|
+
import storage from '../../storage/SessionStorage.js';
|
|
3
|
+
|
|
4
|
+
const realmPathTemplate = '/realms/%s';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get current realm path
|
|
8
|
+
* @returns {String} a CREST-compliant realm path, e.g. /realms/root/realms/alpha
|
|
9
|
+
*/
|
|
10
|
+
export function getCurrentRealmPath() {
|
|
11
|
+
let realm = storage.session.getRealm();
|
|
12
|
+
if (realm.startsWith('/') && realm.length > 1) {
|
|
13
|
+
realm = realm.substring(1);
|
|
14
|
+
}
|
|
15
|
+
let realmPath = util.format(realmPathTemplate, 'root');
|
|
16
|
+
if (realm !== '/') {
|
|
17
|
+
realmPath += util.format(realmPathTemplate, realm);
|
|
18
|
+
}
|
|
19
|
+
return realmPath;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get current realm name
|
|
24
|
+
* @returns {String} name of the current realm. /alpha -> alpha
|
|
25
|
+
*/
|
|
26
|
+
export function getCurrentRealmName() {
|
|
27
|
+
const realm = storage.session.getRealm();
|
|
28
|
+
const components = realm.split('/');
|
|
29
|
+
let realmName = '/';
|
|
30
|
+
if (components.length > 0 && realmName !== realm) {
|
|
31
|
+
realmName = components[components.length - 1];
|
|
32
|
+
}
|
|
33
|
+
return realmName;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get current realm name
|
|
38
|
+
* @param {String} realm realm
|
|
39
|
+
* @returns {String} name of the realm. /alpha -> alpha
|
|
40
|
+
*/
|
|
41
|
+
export function getRealmName(realm) {
|
|
42
|
+
const components = realm.split('/');
|
|
43
|
+
let realmName = '/';
|
|
44
|
+
if (components.length > 0 && realmName !== realm) {
|
|
45
|
+
realmName = components[components.length - 1];
|
|
46
|
+
}
|
|
47
|
+
return realmName;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get tenant base URL
|
|
52
|
+
* @param {String} tenant tenant URL with path and query params
|
|
53
|
+
* @returns {String} tenant base URL without path and query params
|
|
54
|
+
*/
|
|
55
|
+
export function getTenantURL(tenant) {
|
|
56
|
+
const parsedUrl = new URL(tenant);
|
|
57
|
+
return `${parsedUrl.protocol}//${parsedUrl.host}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Deep delete keys and their values from an input object. If a key in object contains substring, the key an its value is deleted.
|
|
62
|
+
* @param {Object} object input object that needs keys removed
|
|
63
|
+
* @param {String} substring substring to search for in key
|
|
64
|
+
* @returns the modified object without the matching keys and their values
|
|
65
|
+
*/
|
|
66
|
+
export function deleteDeepByKey(object, substring) {
|
|
67
|
+
const obj = object;
|
|
68
|
+
const keys = Object.keys(obj);
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
if (key.indexOf(substring) > 0) {
|
|
71
|
+
delete obj[key];
|
|
72
|
+
} else if (Object(obj[key]) === obj[key]) {
|
|
73
|
+
obj[key] = deleteDeepByKey(obj[key], substring);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return obj;
|
|
77
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCurrentRealmPath,
|
|
3
|
+
getTenantURL,
|
|
4
|
+
} from './ApiUtils.js';
|
|
5
|
+
import sessionStorage from '../../storage/SessionStorage.js';
|
|
6
|
+
|
|
7
|
+
test.skip('replaceAll should be deleted because it works like native String.replaceAll', () => {
|
|
8
|
+
// Arrange
|
|
9
|
+
// Act
|
|
10
|
+
// Assert
|
|
11
|
+
expect(true).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('getCurrentRealmPath should prepend realmPath to specified realm', () => {
|
|
15
|
+
// Arrange
|
|
16
|
+
const REALM_PATH = 'alpha';
|
|
17
|
+
sessionStorage.session.setItem('realm', REALM_PATH);
|
|
18
|
+
// Act
|
|
19
|
+
const testString = getCurrentRealmPath(REALM_PATH);
|
|
20
|
+
// Assert
|
|
21
|
+
expect(testString).toBe('/realms/root/realms/alpha');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('getCurrentRealmPath should prepend realmPath to specified realm with leading slash', () => {
|
|
25
|
+
// Arrange
|
|
26
|
+
const REALM_PATH = '/alpha';
|
|
27
|
+
sessionStorage.session.setItem('realm', REALM_PATH);
|
|
28
|
+
// Act
|
|
29
|
+
const testString = getCurrentRealmPath(REALM_PATH);
|
|
30
|
+
// Assert
|
|
31
|
+
expect(testString).toBe('/realms/root/realms/alpha');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('getCurrentRealmPath "/" should resolve to root', () => {
|
|
35
|
+
// Arrange
|
|
36
|
+
const REALM_PATH = '/';
|
|
37
|
+
sessionStorage.session.setItem('realm', REALM_PATH);
|
|
38
|
+
// Act
|
|
39
|
+
const testString = getCurrentRealmPath(REALM_PATH);
|
|
40
|
+
// Assert
|
|
41
|
+
expect(testString).toBe('/realms/root');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('getCurrentRealmPath should not handle multiple leading slash', () => {
|
|
45
|
+
// Arrange
|
|
46
|
+
const REALM_PATH = '//alpha';
|
|
47
|
+
sessionStorage.session.setItem('realm', REALM_PATH);
|
|
48
|
+
// Act
|
|
49
|
+
const testString = getCurrentRealmPath(REALM_PATH);
|
|
50
|
+
// Assert
|
|
51
|
+
expect(testString).toBe('/realms/root/realms//alpha');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('getCurrentRealmPath should not handle nested depth realms', () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const REALM_PATH = '/alpha/erm';
|
|
57
|
+
sessionStorage.session.setItem('realm', REALM_PATH);
|
|
58
|
+
// Act
|
|
59
|
+
const testString = getCurrentRealmPath(REALM_PATH);
|
|
60
|
+
// Assert
|
|
61
|
+
expect(testString).toBe('/realms/root/realms/alpha/erm');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('getTenantURL should parse the https protocol and the hostname', () => {
|
|
65
|
+
// Arrange
|
|
66
|
+
const URL_WITH_TENANT =
|
|
67
|
+
'https://example.frodo.com/am/ui-admin/#realms/%2Falpha/dashboard';
|
|
68
|
+
|
|
69
|
+
// Act
|
|
70
|
+
const parsed = getTenantURL(URL_WITH_TENANT);
|
|
71
|
+
|
|
72
|
+
// Assert
|
|
73
|
+
expect(parsed).toBe('https://example.frodo.com');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('getTenantURL should not validate protocol', () => {
|
|
77
|
+
// Arrange
|
|
78
|
+
const URL_WITH_TENANT =
|
|
79
|
+
'ftp://example.frodo.com/am/ui-admin/#realms/%2Falpha/dashboard';
|
|
80
|
+
// Act
|
|
81
|
+
const parsed = getTenantURL(URL_WITH_TENANT);
|
|
82
|
+
// Assert
|
|
83
|
+
expect(parsed).toBe('ftp://example.frodo.com');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('getTenantURL Invalid URL should throw', () => {
|
|
87
|
+
// Arrange
|
|
88
|
+
const URL_WITH_TENANT =
|
|
89
|
+
'//:example.frodo.com/am/ui-admin/#realms/%2Falpha/dashboard';
|
|
90
|
+
// Act
|
|
91
|
+
const trap = () => {
|
|
92
|
+
getTenantURL(URL_WITH_TENANT);
|
|
93
|
+
};
|
|
94
|
+
// Assert
|
|
95
|
+
expect(trap).toThrow('Invalid URL');
|
|
96
|
+
});
|