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

Commit 00a0ef7

Browse files
Merge pull request #33 from solace-iot-team/feature-pub-dest
Feature pub dest
2 parents 7f79d3f + b03f640 commit 00a0ef7

File tree

78 files changed

+3285
-3869
lines changed

Some content is hidden

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

78 files changed

+3285
-3869
lines changed

ReleaseNotes.md

+23
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
Solace Async API Management.
44

5+
## Version 0.2.2
6+
* [API-M Admin & Developer Portal](https://github.com/solace-iot-team/async-apim/tree/main/apim-portal): 0.2.2
7+
* [API-M Server OpenAPI](https://github.com/solace-iot-team/async-apim/blob/main/apim-server/server/common/api.yml): 0.2.1
8+
* [API-M Server](https://github.com/solace-iot-team/async-apim/tree/main/apim-server): 0.2.1
9+
* [API-M Connector OpenAPI](https://github.com/solace-iot-team/platform-api): 0.7.15
10+
11+
**New Features:**
12+
* **AdminPortal:API Products**
13+
- added Cloning of API Product
14+
- creates a deep copy of existing api product
15+
- resets the stage to 'draft' and sets createdBy to logged-in user
16+
- keeps a record of which api product it was derived from
17+
**Enhancements:**
18+
* **AdminPortal:API Products**
19+
- renamed version to revision to more accurately reflect semantics
20+
- new attribute: _AP_PUBLISH_DESTINATION_="{comma separated list of externalSystemIds}"
21+
- attribute is not present if api product is not published
22+
- added as part of access & state management for api product
23+
- reworked edit api product component: user can now select each tab individually and update api product in tab
24+
- every save results in a new revision of the api product
25+
* **External Systems**
26+
- added flag to indicate if the external system is a marketplace or not
27+
528
## Version 0.2.1
629
* [API-M Admin & Developer Portal](https://github.com/solace-iot-team/async-apim/tree/main/apim-portal): 0.2.1
730
* [API-M Server OpenAPI](https://github.com/solace-iot-team/async-apim/blob/main/apim-server/server/common/api.yml): 0.2.0

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.2.1",
3+
"version": "0.2.2",
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.7.11",
27+
"@solace-iot-team/apim-connector-openapi-browser": "^0.7.15",
2828
"async-mutex": "^0.3.2",
2929
"base-64": "^1.0.0",
3030
"js-yaml": "^4.1.0",

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

-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import { ManageSystemOrganizationsPage } from "./pages/ManageSystemOrganizations
2626
import { ManageApiProductsPage } from "./pages/ManageApiProductsPage";
2727

2828
// DELETEME
29-
import { ManageApiProductsPage as deleteme_ManageApiProductsPage} from "./pages/deleteme.ManageApiProductsPage";
3029
import { ManageAppsPage as deleteme_ManageAppsPage} from './pages/deleteme.ManageAppsPage';
3130
import { ManageSystemOrganizationsPage as deleteme_ManageSystemOrganizationsPage } from "./pages/deleteme_ManageSystemOrganizationsPage";
3231
import { MonitorOrganizationStatusPage as deleteme_MonitorOrganizationStatusPage } from "./pages/deleteme_MonitorOrganizationStatusPage";
@@ -66,9 +65,6 @@ export const AdminPortalAppRoutes = (): Array<JSX.Element> => {
6665
/* API Team*/
6766
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationApis} component={ManageApisPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationApis} />,
6867
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationApiProducts} component={ManageApiProductsPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationApiProducts} />,
69-
70-
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.deleteme_ManageOrganizationApiProducts} component={deleteme_ManageApiProductsPage} exact key={EUIAdminPortalResourcePaths.deleteme_ManageOrganizationApiProducts} />,
71-
7268
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.ManageOrganizationApps} component={ManageAppsPage} exact key={EUIAdminPortalResourcePaths.ManageOrganizationApps} />,
7369

7470
<ProtectedRouteWithRbacAndOrgAccess path={EUIAdminPortalResourcePaths.DELETEME_ManageOrganizationApps} component={deleteme_ManageAppsPage} exact key={EUIAdminPortalResourcePaths.DELETEME_ManageOrganizationApps} />,

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

-6
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export const AdminPortalSideBar: React.FC<IAdminPortalSideBarProps> = (props: IA
5050
isDisabled(EUIAdminPortalResourcePaths.ManageOrganizationApiProducts) &&
5151

5252
isDisabled(EUIAdminPortalResourcePaths.DELETEME_ManageOrganizationApps) &&
53-
isDisabled(EUIAdminPortalResourcePaths.deleteme_ManageOrganizationApiProducts) &&
5453

5554
isDisabled(EUIAdminPortalResourcePaths.ManageOrganizationApis)
5655
) return [];
@@ -71,11 +70,6 @@ export const AdminPortalSideBar: React.FC<IAdminPortalSideBarProps> = (props: IA
7170
disabled: isDisabledWithConnectorUnavailable(isDisabledWithoutOrg, EUIAdminPortalResourcePaths.ManageOrganizationApiProducts),
7271
command: () => { navigateTo(EUIAdminPortalResourcePaths.ManageOrganizationApiProducts); }
7372
},
74-
// {
75-
// label: 'OLD API Products',
76-
// disabled: isDisabledWithConnectorUnavailable(isDisabledWithoutOrg, EUIAdminPortalResourcePaths.deleteme_ManageOrganizationApiProducts),
77-
// command: () => { navigateTo(EUIAdminPortalResourcePaths.deleteme_ManageOrganizationApiProducts); }
78-
// },
7973
{
8074
label: 'APIs',
8175
disabled: isDisabledWithConnectorUnavailable(isDisabledWithoutOrg, EUIAdminPortalResourcePaths.ManageOrganizationApis),

apim-portal/src/admin-portal/components/MaintainApiProducts/ListMaintainApiProducts.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,15 @@ export const ListMaintainApiProducts: React.FC<IListMaintainApiProductsProps> =
172172
return row.apAccessLevel;
173173
}
174174
const stateTemplate = (row: TManagedObject): string => {
175-
return row.apLifecycleInfo.apLifecycleState;
175+
return row.apLifecycleStageInfo.stage;
176176
}
177177
const renderManagedObjectDataTable = () => {
178178
const dataKey = APAdminPortalApiProductsDisplayService.nameOf_ApEntityId('id');
179179
const sortField = APAdminPortalApiProductsDisplayService.nameOf_ApEntityId('displayName');
180180
const filterField = APAdminPortalApiProductsDisplayService.nameOf<TAPAdminPortalApiProductDisplay>('apSearchContent');
181181
// const approvalTypeSortField = APAdminPortalApiProductsDisplayService.nameOf<TAPAdminPortalApiProductDisplay>('apApprovalType');
182182
const accessLevelSortField = APAdminPortalApiProductsDisplayService.nameOf<TAPAdminPortalApiProductDisplay>('apAccessLevel');
183-
const stateSortField = APAdminPortalApiProductsDisplayService.nameOf_ApLifecycleInfo('apLifecycleState');
183+
const stateSortField = APAdminPortalApiProductsDisplayService.nameOf_ApLifecycleStageInfo('stage');
184184
const businessGroupSortField = APAdminPortalApiProductsDisplayService.nameOf_ApBusinessGroupInfo_ApBusinessGroupDisplayReference_ApEntityId('displayName');
185185
return (
186186
<div className="card">

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

+10
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ export const MaintainApiProducts: React.FC<IMaintainApiProductsProps> = (props:
9797
}
9898
}, [apiCallStatus]); /* eslint-disable-line react-hooks/exhaustive-deps */
9999

100+
// * initialized object *
101+
const onInitializedManagedObject = (apAdminPortalApiProductDisplay: TAPAdminPortalApiProductDisplay) => {
102+
setManagedObject_AllowedActions(APAdminPortalApiProductsDisplayService.get_AllowedActions({
103+
apAdminPortalApiProductDisplay: apAdminPortalApiProductDisplay,
104+
authorizedResourcePathAsString: authContext.authorizedResourcePathsAsString,
105+
userId: userContext.apLoginUserDisplay.apEntityId.id,
106+
userBusinessGroupId: userContext.runtimeSettings.currentBusinessGroupEntityId?.id
107+
}));
108+
}
100109
// * View Object *
101110
const onViewManagedObject = (apAdminPortalApiProductDisplay: TAPAdminPortalApiProductDisplay): void => {
102111
setApiCallStatus(null);
@@ -272,6 +281,7 @@ export const MaintainApiProducts: React.FC<IMaintainApiProductsProps> = (props:
272281
key={`${ComponentName}_showViewComponent_${refreshCounter}`}
273282
organizationId={props.organizationEntityId.id}
274283
apiProductEntityId={managedObjectEntityId}
284+
onInitialized={onInitializedManagedObject}
275285
onSuccess={onSubComponentUserNotification}
276286
onError={onSubComponentError}
277287
onLoadingChange={setIsLoading}

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

+53-29
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,22 @@ import APEntityIdsService, {
2020
import APAdminPortalApiProductsDisplayService, { TAPAdminPortalApiProductDisplay } from "../../displayServices/APAdminPortalApiProductsDisplayService";
2121
import { E_CALL_STATE_ACTIONS } from "./ManageApiProductsCommon";
2222
import APApiSpecsDisplayService, { TAPApiSpecDisplay } from "../../../displayServices/APApiSpecsDisplayService";
23-
import { TAPManagedAssetBusinessGroupInfo, TAPManagedAssetLifecycleInfo } from "../../../displayServices/APManagedAssetDisplayService";
23+
import { TAPManagedAssetBusinessGroupInfo, TAPManagedAssetPublishDestinationInfo } from "../../../displayServices/APManagedAssetDisplayService";
2424
import { APDisplayApAttributeDisplayList } from "../../../components/APDisplay/APDisplayApAttributeDisplayList";
2525
import { APDisplayApControlledChannelParameters } from "../../../components/APDisplay/APDisplayApControlledChannelParameters";
2626
import { Config } from "../../../Config";
2727
import { APDisplayApisDetails } from "../../../components/APDisplay/APDisplayApisDetails";
2828
import { Globals } from "../../../utils/Globals";
2929
import APVersioningDisplayService from "../../../displayServices/APVersioningDisplayService";
3030
import APMetaInfoDisplayService from "../../../displayServices/APMetaInfoDisplayService";
31-
import { APIProductAccessLevel } from "@solace-iot-team/apim-connector-openapi-browser";
31+
import { APIProductAccessLevel, MetaEntityReference } from "@solace-iot-team/apim-connector-openapi-browser";
3232
import { TAPAttributeDisplayList } from "../../../displayServices/APAttributesDisplayService/APAttributesDisplayService";
3333
import { APDisplayBusinessGroupInfo } from "../../../components/APDisplay/APDisplayBusinessGroupInfo";
34+
import { IAPLifecycleStageInfo } from "../../../displayServices/APLifecycleStageInfoDisplayService";
3435

3536
import '../../../components/APComponents.css';
3637
import "./ManageApiProducts.css";
38+
import { OrganizationContext } from "../../../components/APContextProviders/APOrganizationContextProvider";
3739

3840
export enum E_DISPLAY_ADMIN_PORTAL_API_PRODUCT_SCOPE {
3941
REVIEW_AND_CREATE = "REVIEW_AND_CREATE",
@@ -61,20 +63,24 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
6163
const [showApiSpecRefreshCounter, setShowApiSpecRefreshCounter] = React.useState<number>(0);
6264
const [tabActiveIndex, setTabActiveIndex] = React.useState(0);
6365
// version
64-
const [selectedVersion, setSelectedVersion] = React.useState<string>();
66+
const [selectedRevision, setSelectedRevision] = React.useState<string>();
67+
68+
const [organizationContext] = React.useContext(OrganizationContext);
69+
const IsSingleApiSelection: boolean = organizationContext.apMaxNumApis_Per_ApiProduct === 1;
70+
const ApiTabHeader: string = IsSingleApiSelection ? "API" : "API(s)";
6571

6672
// * Api Calls *
67-
const apiGetManagedObject = async(version: string): Promise<TApiCallState> => {
73+
const apiGetManagedObject = async(revision: string): Promise<TApiCallState> => {
6874
const funcName = 'apiGetManagedObject';
6975
const logName = `${ComponentName}.${funcName}()`;
70-
let callState: TApiCallState = ApiCallState.getInitialCallState(E_CALL_STATE_ACTIONS.API_GET_API_PRODUCT, `retrieve details for api product: ${props.apAdminPortalApiProductDisplay.apEntityId.displayName}, version: ${version}`);
76+
let callState: TApiCallState = ApiCallState.getInitialCallState(E_CALL_STATE_ACTIONS.API_GET_API_PRODUCT, `retrieve details for api product: ${props.apAdminPortalApiProductDisplay.apEntityId.displayName}, revision: ${revision}`);
7177
try {
7278
const object: TAPAdminPortalApiProductDisplay = await APAdminPortalApiProductsDisplayService.apiGet_AdminPortalApApiProductDisplay({
7379
organizationId: props.organizationId,
7480
apiProductId: props.apAdminPortalApiProductDisplay.apEntityId.id,
7581
default_ownerId: props.apAdminPortalApiProductDisplay.apOwnerInfo.id,
7682
fetch_revision_list: true,
77-
revision: version,
83+
revision: revision,
7884
});
7985
setManagedObject(object);
8086
} catch(e) {
@@ -122,9 +128,9 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
122128
setManagedObject(props.apAdminPortalApiProductDisplay);
123129
}
124130

125-
const doFetchVersion = async (version: string) => {
131+
const doFetchRevision = async (revision: string) => {
126132
props.onLoadingChange(true);
127-
await apiGetManagedObject(version);
133+
await apiGetManagedObject(revision);
128134
props.onLoadingChange(false);
129135
}
130136

@@ -136,14 +142,14 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
136142

137143
React.useEffect(() => {
138144
if(managedObject === undefined) return;
139-
setSelectedVersion(managedObject.apVersionInfo.apCurrentVersion);
145+
setSelectedRevision(managedObject.apVersionInfo.apCurrentVersion);
140146
}, [managedObject]); /* eslint-disable-line react-hooks/exhaustive-deps */
141147

142148
React.useEffect(() => {
143-
if(selectedVersion === undefined) return;
149+
if(selectedRevision === undefined) return;
144150
if(managedObject === undefined) return;
145-
if(selectedVersion !== managedObject.apVersionInfo.apCurrentVersion) doFetchVersion(selectedVersion);
146-
}, [selectedVersion]); /* eslint-disable-line react-hooks/exhaustive-deps */
151+
if(selectedRevision !== managedObject.apVersionInfo.apCurrentVersion) doFetchRevision(selectedRevision);
152+
}, [selectedRevision]); /* eslint-disable-line react-hooks/exhaustive-deps */
147153

148154
React.useEffect(() => {
149155
if (apiCallStatus !== null) {
@@ -255,46 +261,46 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
255261
return (<></>);
256262
}
257263

258-
const renderVersionSelect = (): JSX.Element => {
259-
const funcName = 'renderVersionSelect';
264+
const renderRevisionSelect = (): JSX.Element => {
265+
const funcName = 'renderRevisionSelect';
260266
const logName = `${ComponentName}.${funcName}()`;
261267
if(managedObject === undefined) throw new Error(`${logName}: managedObject is undefined`);
262268
if(managedObject.apVersionInfo.apVersionList === undefined) throw new Error(`${logName}: managedObject.apVersionInfo.apVersionList is undefined`);
263269

264-
const onVersionSelect = (e: DropdownChangeParams) => {
265-
setSelectedVersion(e.value);
270+
const onRevisionSelect = (e: DropdownChangeParams) => {
271+
setSelectedRevision(e.value);
266272
}
267273

268274
return(
269275
<Dropdown
270-
value={selectedVersion}
276+
value={selectedRevision}
271277
options={APVersioningDisplayService.get_Sorted_ApVersionList(managedObject.apVersionInfo.apVersionList)}
272-
onChange={onVersionSelect}
278+
onChange={onRevisionSelect}
273279
/>
274280
);
275281
}
276282

277-
const renderVersion = (mo: TManagedObject): JSX.Element => {
278-
const funcName = 'renderVersion';
283+
const renderRevision = (mo: TManagedObject): JSX.Element => {
284+
const funcName = 'renderRevision';
279285
const logName = `${ComponentName}.${funcName}()`;
280286
switch(props.scope) {
281287
case E_DISPLAY_ADMIN_PORTAL_API_PRODUCT_SCOPE.VIEW_EXISTING:
282288
return (
283-
<div><b>Version: </b>{renderVersionSelect()}</div>
289+
<div><b>Revision: </b>{renderRevisionSelect()}</div>
284290
);
285291
case E_DISPLAY_ADMIN_PORTAL_API_PRODUCT_SCOPE.REVIEW_AND_CREATE:
286292
return (
287-
<div><b>New Version: {mo.apVersionInfo.apCurrentVersion}</b></div>
293+
<div><b>New Revision: {mo.apVersionInfo.apCurrentVersion}</b></div>
288294
);
289295
default:
290296
Globals.assertNever(logName, props.scope);
291297
}
292298
return (<></>);
293299
}
294300

295-
const renderState = (apManagedAssetLifecycleInfo: TAPManagedAssetLifecycleInfo): JSX.Element => {
301+
const renderState = (apLifecycleStageInfo: IAPLifecycleStageInfo): JSX.Element => {
296302
return(
297-
<span><b>State: </b>{apManagedAssetLifecycleInfo.apLifecycleState}</span>
303+
<span><b>State: </b>{apLifecycleStageInfo.stage}</span>
298304
);
299305
}
300306

@@ -304,6 +310,16 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
304310
);
305311
}
306312

313+
const renderPublishDestinationInfo = (apPublishDestinationInfo: TAPManagedAssetPublishDestinationInfo): JSX.Element => {
314+
const renderValue = (apExternalSystemEntityIdList: TAPEntityIdList): string => {
315+
if(apExternalSystemEntityIdList.length === 0) return 'Not Published.';
316+
return APEntityIdsService.create_SortedDisplayNameList(apExternalSystemEntityIdList).join(', ');
317+
}
318+
return(
319+
<span><b>Publish Destination(s): </b>{renderValue(apPublishDestinationInfo.apExternalSystemEntityIdList)}</span>
320+
);
321+
}
322+
307323
const renderHeader = (mo: TManagedObject): JSX.Element => {
308324
return (
309325
<div className="p-col-12">
@@ -312,15 +328,16 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
312328

313329
<div>{renderBusinessGroupInfo(mo.apBusinessGroupInfo)}</div>
314330
<div>{renderOwner(mo.apOwnerInfo)}</div>
315-
<div>{renderState(mo.apLifecycleInfo)}</div>
331+
<div>{renderState(mo.apLifecycleStageInfo)}</div>
316332
<div>{renderAccessLevel(mo.apAccessLevel)}</div>
333+
<div>{renderPublishDestinationInfo(mo.apPublishDestinationInfo)}</div>
317334

318335
{/* DEBUG */}
319336
{/* <div><b>DEVEL: Current Version</b>: {mo.apVersionInfo.apCurrentVersion}, Last Version: {mo.apVersionInfo.apLastVersion}</div> */}
320337

321338
</div>
322339
<div className="api-product-view-detail-right">
323-
<div>{renderVersion(mo)}</div>
340+
<div>{renderRevision(mo)}</div>
324341
</div>
325342
</div>
326343
</div>
@@ -329,12 +346,19 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
329346

330347
const renderMeta = (mo: TManagedObject): JSX.Element => {
331348
if(props.scope === E_DISPLAY_ADMIN_PORTAL_API_PRODUCT_SCOPE.REVIEW_AND_CREATE) return (<></>);
349+
const renderDerivedFrom = (derivedFrom?: MetaEntityReference): JSX.Element => {
350+
if(derivedFrom === undefined) return (<></>);
351+
const _name: string | undefined = derivedFrom.displayName !== undefined ? derivedFrom.displayName : derivedFrom.name;
352+
return (<div>Cloned from: {`${_name} (${derivedFrom.revision})`}</div>);
353+
}
332354
return (
333355
<React.Fragment>
334356
<div>Created by: {mo.apMetaInfo.apCreatedBy}</div>
335357
<div>Created on: {APMetaInfoDisplayService.create_Timestamp_DisplayString(mo.apMetaInfo.apCreatedOn)}</div>
336358
<div>Last Modified by: {mo.apMetaInfo.apLastModifiedBy}</div>
337-
<div>Last Modifined on: {APMetaInfoDisplayService.create_Timestamp_DisplayString(mo.apMetaInfo.apLastModifiedOn)}</div>
359+
<div>Last Modified on: {APMetaInfoDisplayService.create_Timestamp_DisplayString(mo.apMetaInfo.apLastModifiedOn)}</div>
360+
{ renderDerivedFrom(mo.apMetaInfo.apDerivedFrom) }
361+
{/* <div>Clones: TODO: list of id/displaynames? - needs another API call</div> */}
338362
</React.Fragment>
339363
);
340364
}
@@ -395,7 +419,7 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
395419
</div>
396420
</React.Fragment>
397421
</TabPanel>
398-
<TabPanel header='APIs'>
422+
<TabPanel header={ApiTabHeader}>
399423
<React.Fragment>
400424
<div>
401425
{renderShowApiButtons()}
@@ -481,7 +505,7 @@ export const DisplayAdminPortalApiProduct: React.FC<IDisplayAdminPortalApiProduc
481505
{/* <div>DEBUG: selectedVersion = '{selectedVersion}'</div>
482506
<div>DEBUG: managedObject.apVersionInfo={JSON.stringify(managedObject?.apVersionInfo)}</div> */}
483507

484-
{managedObject && selectedVersion !== undefined && renderManagedObject() }
508+
{managedObject && selectedRevision !== undefined && renderManagedObject() }
485509

486510
</div>
487511
</React.Fragment>

0 commit comments

Comments
 (0)