Skip to content
This repository was archived by the owner on Feb 20, 2024. It is now read-only.

Commit c04cc81

Browse files
Merge pull request #15 from solace-iot-team/feature-upgrade-conn-6.5
Feature upgrade conn 6.5
2 parents a36c84b + 5a96a17 commit c04cc81

File tree

106 files changed

+2132
-1095
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+2132
-1095
lines changed

ReleaseNotes.md

+34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,40 @@
22

33
Solace Async API Management.
44

5+
## Version 0.0.34
6+
* [API-M Admin & Developer Portal](https://github.com/solace-iot-team/async-apim/tree/main/apim-portal): 0.0.34
7+
* [API-M Server OpenAPI](https://github.com/solace-iot-team/async-apim/blob/main/apim-server/server/common/api.yml): 0.0.17
8+
* [API-M Server](https://github.com/solace-iot-team/async-apim/tree/main/apim-server): 0.0.11
9+
* [API-M Connector OpenAPI](https://github.com/solace-iot-team/platform-api): 0.6.5
10+
11+
#### API-M Admin & Developer Portal
12+
13+
**Enhancements:**
14+
- **Organization Status**
15+
- display connectivity status of the organization
16+
- **Edit/New Organization**
17+
- advanced configuration: event portal config is now optional
18+
- **Import APIs from Event Portal**
19+
- disabled if no event portal connectivity
20+
- **Organzation Display Name**
21+
- use display name instead of id
22+
- **Admin Portal: Manage API Products**
23+
- added `accessLevel` property, defaults to `private`
24+
- **Developer Portal: Explore API Products**
25+
- added `accessLevel` to view & search facility
26+
27+
**Fixes:**
28+
- **Developer App: Manage Webhooks**
29+
- now takes into account the reversal of pub v. sub permissions - these are now the same as the spec
30+
- **Login without available connector**
31+
- login as a user with `systemAdmin` role works now regardless of connector config/availability
32+
- **Developer Portal: My Apps**
33+
- issue: invalid format of portal user as a connector developer
34+
- portal now uses the same validation as connector for first/last name
35+
- in case of an error, portal displays error message on page instead of blank page
36+
37+
38+
539
## Version 0.0.33
640
* [API-M Admin & Developer Portal](https://github.com/solace-iot-team/async-apim/tree/main/apim-portal): 0.0.33
741
* [API-M Server OpenAPI](https://github.com/solace-iot-team/async-apim/blob/main/apim-server/server/common/api.yml): 0.0.16

apim-portal/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "async-apim-portal",
3-
"version": "0.0.33",
3+
"version": "0.0.34",
44
"description": "Solace Async API Management Portal",
55
"repository": {
66
"type": "git",
@@ -24,7 +24,7 @@
2424
},
2525
"dependencies": {
2626
"@asyncapi/react-component": "1.0.0-next.33",
27-
"@solace-iot-team/apim-connector-openapi-browser": "^0.6.1",
27+
"@solace-iot-team/apim-connector-openapi-browser": "^0.6.5",
2828
"async-mutex": "^0.3.2",
2929
"base-64": "^1.0.0",
3030
"js-yaml": "^4.1.0",

apim-portal/src/App.tsx

+57-12
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@ import { AdminPortalSideBar } from "./admin-portal/components/AdminPortalSideBar
1212
// * Developer Portal *
1313
import { DeveloperPortalHomePage } from "./developer-portal/pages/DeveloperPortalHomePage";
1414
import { DeveloperPortalAppRoutes } from "./developer-portal/DeveloperPortalAppRoutes";
15-
import { DeveloperPortalSideBar } from "./developer-portal/components/DeveloperPortalSideBar/DeveloperPortalSideBar";
15+
import { DeveloperPortalSideBar } from "./developer-portal/components/DeveloperPortalSideBar";
16+
// * Public Developer Portal *
17+
import { PublicDeveloperPortalAppRoutes } from "./developer-portal/PublicDeveloperPortalAppRoutes";
18+
import { PublicDeveloperPortalSideBar } from "./developer-portal/components/PublicDeveloperPortalSideBar";
1619

1720
import {
1821
EUIDeveloperToolsResourcePaths,
1922
EUICommonResourcePaths,
2023
EUIAdminPortalResourcePaths,
2124
EUIDeveloperPortalResourcePaths,
2225
EAppState,
23-
TLocationStateAppState,
26+
TLocationStateAppState,
27+
Globals,
2428
} from './utils/Globals';
2529
import { ProtectedRouteWithRbac } from "./auth/ProtectedRouteWithRbac";
2630
import { HomePage } from './pages/HomePage';
@@ -42,6 +46,7 @@ import 'primereact/resources/primereact.min.css';
4246
import 'primeicons/primeicons.css';
4347
import 'primeflex/primeflex.css';
4448
import './App.css';
49+
import { PerformSystemHealthCheck } from "./components/SystemHealth/PerformSystemHealthCheck";
4550

4651
const App: React.FC = () => {
4752
const componentName = 'App';
@@ -52,6 +57,7 @@ const App: React.FC = () => {
5257
const [authContext] = React.useContext(AuthContext);
5358
const [userContext, dispatchUserContextAction] = React.useContext(UserContext);
5459
const [showDeveloperPortal, setShowDeveloperPortal] = React.useState<boolean>(false);
60+
const [showPublicDeveloperPortal, setShowPublicDeveloperPortal] = React.useState<boolean>(false);
5561
const [showAdminPortal, setShowAdminPortal] = React.useState<boolean>(false);
5662
const [showDeveloperTools] = React.useState<boolean>(Config.getUseDevelTools());
5763
const appPortalHistory = useHistory<TLocationStateAppState>();
@@ -67,7 +73,7 @@ const App: React.FC = () => {
6773

6874
React.useEffect(() => {
6975
calculateShowStates(userContext.currentAppState);
70-
}, [userContext.currentAppState]);
76+
}, [userContext.currentAppState, authContext]);
7177

7278
const onSwitchToDeveloperPortal = () => {
7379
const funcName = 'onSwitchToDeveloperPortal';
@@ -90,34 +96,70 @@ const App: React.FC = () => {
9096
}
9197

9298
const calculateShowStates = (appState: EAppState) => {
93-
if(appState === EAppState.ADMIN_PORTAL) {
94-
setShowAdminPortal(true);
95-
setShowDeveloperPortal(false);
96-
}
97-
else if (appState === EAppState.DEVELOPER_PORTAL) {
98-
setShowAdminPortal(false);
99-
setShowDeveloperPortal(true);
100-
}
99+
const funcName = 'calculateShowStates';
100+
const logName = `${componentName}.${funcName}()`;
101+
switch(appState) {
102+
case EAppState.ADMIN_PORTAL:
103+
setShowAdminPortal(true);
104+
setShowDeveloperPortal(false);
105+
setShowPublicDeveloperPortal(false);
106+
break;
107+
case EAppState.DEVELOPER_PORTAL:
108+
setShowAdminPortal(false);
109+
setShowDeveloperPortal(true);
110+
setShowPublicDeveloperPortal(false);
111+
break;
112+
case EAppState.PUBLIC_DEVELOPER_PORTAL:
113+
setShowAdminPortal(false);
114+
setShowDeveloperPortal(false);
115+
setShowPublicDeveloperPortal(true);
116+
break;
117+
case EAppState.UNDEFINED:
118+
setShowAdminPortal(false);
119+
setShowDeveloperPortal(false);
120+
setShowPublicDeveloperPortal(false);
121+
break;
122+
default:
123+
Globals.assertNever(logName, appState);
124+
}
125+
// if(appState === EAppState.ADMIN_PORTAL) {
126+
// setShowAdminPortal(true);
127+
// setShowDeveloperPortal(false);
128+
// }
129+
// else if (appState === EAppState.DEVELOPER_PORTAL) {
130+
// setShowAdminPortal(false);
131+
// setShowDeveloperPortal(true);
132+
// } else {
133+
// setShowAdminPortal(false);
134+
// setShowDeveloperPortal(false);
135+
// }
101136
}
102137

103138
const displayStateInfo = () => {
104139
return (
105140
<p>
141+
authContext.isLoggedIn={String(authContext.isLoggedIn)},
142+
authContext.authorizedResourcePathsAsString.length={authContext.authorizedResourcePathsAsString.length},
106143
userContext.originAppState={userContext.originAppState},
107144
userContext.currentAppState={userContext.currentAppState},
108145
showDeveloperPortal={String(showDeveloperPortal)},
109-
showAdminPortal={String(showAdminPortal)}
146+
showAdminPortal={String(showAdminPortal)},
147+
showPublicDeveloperPortal={String(showPublicDeveloperPortal)}
110148
</p>
111149
);
112150
}
113151

114152
return (
115153
<React.Fragment>
154+
<PerformSystemHealthCheck />
116155
<ShowUserMessage />
117156
<NavBar />
118157
{ isDebug && userContext && displayStateInfo() }
119158
<div className="ap-app-grid">
120159
<div className="ap-app-grid-left">
160+
{showPublicDeveloperPortal &&
161+
<PublicDeveloperPortalSideBar />
162+
}
121163
{showDeveloperPortal &&
122164
<DeveloperPortalSideBar onSwitchToAdminPortal={onSwitchToAdminPortal} />
123165
}
@@ -144,6 +186,9 @@ const App: React.FC = () => {
144186
{/* Admin Portal */}
145187
{ showAdminPortal && AdminPortalAppRoutes() }
146188

189+
{/* Public Developer Portal */}
190+
{ showPublicDeveloperPortal && PublicDeveloperPortalAppRoutes() }
191+
147192
{/* Developer Portal */}
148193
{ showDeveloperPortal && DeveloperPortalAppRoutes() }
149194

apim-portal/src/admin-portal/AdminPortalAppRoutes.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ManageAppsPage } from './pages/ManageAppsPage';
1717
import { AdminPortalHealthCheckViewPage } from "./pages/AdminPortalHealthCheckViewPage";
1818
import { ManageOrgUsersPage } from "./pages/ManageOrgUsersPage";
1919
import { ManageOrgSettingsPage } from "./pages/ManageOrgSettingsPage";
20+
import { MonitorOrgStatusPage } from "./pages/MonitorOrgStatusPage";
2021

2122
export const AdminPortalAppRoutes = (): Array<JSX.Element> => {
2223
// const componentName = 'AdminPortalAppRoutes';
@@ -33,6 +34,7 @@ export const AdminPortalAppRoutes = (): Array<JSX.Element> => {
3334
<ProtectedRouteWithRbac path={EUIAdminPortalResourcePaths.MonitorSystemHealth} component={MonitorSystemHealthPage} exact key={EUIAdminPortalResourcePaths.MonitorSystemHealth} />,
3435
/* Organization */
3536
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationSettings} component={ManageOrgSettingsPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationSettings} />,
37+
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.MonitorOrganizationStatus} component={MonitorOrgStatusPage} exact key={EUIAdminPortalResourcePaths.MonitorOrganizationStatus} />,
3638
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationUsers} component={ManageOrgUsersPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationUsers} />,
3739
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationEnvironments} component={ManageOrgEnvironmentsPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationEnvironments} />,
3840
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationApis} component={ManageApisPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationApis} />,

apim-portal/src/admin-portal/components/AdminPortalSideBar/AdminPortalSideBar.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const AdminPortalSideBar: React.FC<IAdminPortalSideBarProps> = (props: IA
3535
const isDisabledWithoutOrg = (resourcePath: EUICombinedResourcePaths): boolean => {
3636
return (
3737
!AuthHelper.isAuthorizedToAccessResource(authContext.authorizedResourcePathsAsString, resourcePath) ||
38-
!userContext.runtimeSettings.currentOrganizationName
38+
!userContext.runtimeSettings.currentOrganizationEntityId
3939
);
4040
}
4141
const isDisabledWithConnectorUnavailable = (isDisabledFunc: (resourcePath: EUICombinedResourcePaths) => boolean, resourcePath: EUICombinedResourcePaths): boolean => {
@@ -93,6 +93,11 @@ export const AdminPortalSideBar: React.FC<IAdminPortalSideBarProps> = (props: IA
9393
disabled: isDisabledWithConnectorUnavailable(isDisabledWithoutOrg, EUIAdminPortalResourcePaths.ManageOrganizationSettings),
9494
command: () => { navigateTo(EUIAdminPortalResourcePaths.ManageOrganizationSettings); }
9595
},
96+
{
97+
label: 'Status',
98+
disabled: isDisabledWithConnectorUnavailable(isDisabledWithoutOrg, EUIAdminPortalResourcePaths.MonitorOrganizationStatus),
99+
command: () => { navigateTo(EUIAdminPortalResourcePaths.MonitorOrganizationStatus); }
100+
},
96101
]
97102
},
98103
];

apim-portal/src/admin-portal/components/ManageApiProducts/EditNewApiProduct.tsx

+41-4
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
Protocol,
2828
APIParameter,
2929
ClientOptionsGuaranteedMessaging,
30-
CommonEntityNameList
30+
CommonEntityNameList,
3131
} from '@solace-iot-team/apim-connector-openapi-browser';
3232
import { APClientConnectorOpenApi } from "../../../utils/APClientConnectorOpenApi";
3333
import { APConnectorFormValidationRules } from "../../../utils/APConnectorOpenApiFormValidationRules";
@@ -38,6 +38,7 @@ import {
3838
APApiObjectsCommon,
3939
APApiProductsCommon,
4040
APEnvironmentObjectsCommon,
41+
C_DEFAULT_API_PRODUCT_ACCESS_LEVEL,
4142
TAPEnvironmentViewManagedObjectList,
4243
TApiEnvironmentList
4344
} from "../../../components/APApiObjectsCommon";
@@ -148,7 +149,8 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
148149
return {
149150
apiProduct: {
150151
...apiProduct,
151-
approvalType: apiProduct.approvalType ? apiProduct.approvalType : APIProduct.approvalType.AUTO
152+
approvalType: apiProduct.approvalType ? apiProduct.approvalType : APIProduct.approvalType.AUTO,
153+
accessLevel: apiProduct.accessLevel ? apiProduct.accessLevel : C_DEFAULT_API_PRODUCT_ACCESS_LEVEL
152154
},
153155
apiInfoList: apiInfoList,
154156
apiEnvironmentList: apiEnvironmentList,
@@ -157,7 +159,11 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
157159
}
158160
}
159161
const transformManagedObjectToCreateApiObject = (managedObject: TManagedObject): TCreateApiObject => {
160-
return managedObject.apiProduct;
162+
const apiProduct = managedObject.apiProduct;
163+
return {
164+
...apiProduct,
165+
accessLevel: apiProduct.accessLevel ? apiProduct.accessLevel : C_DEFAULT_API_PRODUCT_ACCESS_LEVEL
166+
}
161167
}
162168
const transformManagedObjectToUpdateApiObject = (managedObject: TManagedObject): TUpdateApiObject => {
163169
const apiProduct = managedObject.apiProduct;
@@ -171,7 +177,8 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
171177
protocols: apiProduct.protocols,
172178
pubResources: apiProduct.pubResources,
173179
subResources: apiProduct.subResources,
174-
apis: apiProduct.apis
180+
apis: apiProduct.apis,
181+
accessLevel: apiProduct.accessLevel
175182
}
176183
}
177184
const transformApiEnvironmentListToViewProtocolList = (environmentList: TApiEnvironmentList): TViewProtocolList => {
@@ -258,6 +265,9 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
258265
return apiParameterList;
259266
}
260267
const transformManagedObjectToFormData = (managedObject: TManagedObject): TManagedObjectFormData => {
268+
// const funcName = 'transformManagedObjectToFormData';
269+
// const logName = `${componentName}.${funcName}()`;
270+
// alert(`${logName}: managedObject.apiProduct.accessLevel=${managedObject.apiProduct.accessLevel}`);
261271
const defaultClientOptionsGuaranteedMessaging: ClientOptionsGuaranteedMessaging = {
262272
requireQueue: false,
263273
accessType: ClientOptionsGuaranteedMessaging.accessType.EXCLUSIVE,
@@ -370,6 +380,7 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
370380
const funcName = 'apiUpdateManagedObject';
371381
const logName = `${componentName}.${funcName}()`;
372382
let callState: TApiCallState = ApiCallState.getInitialCallState(E_CALL_STATE_ACTIONS.API_UPDATE_API_PRODUCT, `update api product: ${managedObject.apiProduct.displayName}`);
383+
// alert(`${logName}: managedObject.apiProduct.accessLevel = ${managedObject.apiProduct.accessLevel}`);
373384
try {
374385
await ApiProductsService.updateApiProduct({
375386
organizationName: props.organizationId,
@@ -567,10 +578,12 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
567578
// const funcName = 'doPopulateManagedObjectFormDataValues';
568579
// const logName = `${componentName}.${funcName}()`;
569580
// console.log(`${logName}: managedObjectFormData=${JSON.stringify(managedObjectFormData, null, 2)}`);
581+
// alert(`${logName}: managedObjectFormData.apiProduct.accessLevel=${managedObjectFormData.apiProduct.accessLevel}`);
570582
managedObjectUseForm.setValue('apiProduct.name', managedObjectFormData.apiProduct.name);
571583
managedObjectUseForm.setValue('apiProduct.displayName', managedObjectFormData.apiProduct.displayName);
572584
managedObjectUseForm.setValue('apiProduct.description', managedObjectFormData.apiProduct.description);
573585
managedObjectUseForm.setValue('apiProduct.approvalType', managedObjectFormData.apiProduct.approvalType);
586+
managedObjectUseForm.setValue('apiProduct.accessLevel', managedObjectFormData.apiProduct.accessLevel);
574587
managedObjectUseForm.setValue('apiProduct.attributes', managedObjectFormData.apiProduct.attributes);
575588
// client options: guaranteed messaging
576589
managedObjectUseForm.setValue('clientOptionsGuaranteedMessaging.requireQueue', managedObjectFormData.clientOptionsGuaranteedMessaging.requireQueue);
@@ -1074,6 +1087,30 @@ export const EditNewApiProduct: React.FC<IEditNewApiProductProps> = (props: IEdi
10741087
</span>
10751088
{displayManagedObjectFormFieldErrorMessage(managedObjectUseForm.formState.errors.apiProduct?.approvalType)}
10761089
</div>
1090+
{/* accessLevel */}
1091+
<div className="p-field">
1092+
<span className="p-float-label">
1093+
<Controller
1094+
name="apiProduct.accessLevel"
1095+
control={managedObjectUseForm.control}
1096+
rules={{
1097+
required: "Select access level.",
1098+
}}
1099+
render={( { field, fieldState }) => {
1100+
return(
1101+
<Dropdown
1102+
id={field.name}
1103+
{...field}
1104+
options={APApiProductsCommon.getAccessLevelSelectList()}
1105+
onChange={(e) => field.onChange(e.value)}
1106+
className={classNames({ 'p-invalid': fieldState.invalid })}
1107+
/>
1108+
)}}
1109+
/>
1110+
<label htmlFor="apiProduct.accessLevel" className={classNames({ 'p-error': managedObjectUseForm.formState.errors.apiProduct?.accessLevel })}>Access Level*</label>
1111+
</span>
1112+
{displayManagedObjectFormFieldErrorMessage(managedObjectUseForm.formState.errors.apiProduct?.accessLevel)}
1113+
</div>
10771114
{/* apis */}
10781115
<div className="p-field">
10791116
<span className="p-float-label">

0 commit comments

Comments
 (0)