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

Commit a789e6f

Browse files
Merge pull request #40 from solace-iot-team/feat-ext-creds
Feat ext creds
2 parents 0265cf9 + 095d6dd commit a789e6f

File tree

26 files changed

+756
-68
lines changed

26 files changed

+756
-68
lines changed

ReleaseNotes.md

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

33
Solace Async API Management.
44

5+
## Version 0.3.2
6+
* [API-M Admin & Developer Portal](https://github.com/solace-iot-team/async-apim/tree/main/apim-portal): 0.3.2
7+
* [API-M Server OpenAPI](https://github.com/solace-iot-team/async-apim/blob/main/apim-server/server/common/api.yml): 0.3.1
8+
* [API-M Server](https://github.com/solace-iot-team/async-apim/tree/main/apim-server): 0.3.2
9+
* [API-M Connector OpenAPI](https://github.com/solace-iot-team/platform-api): 0.7.19
10+
11+
**Enhancements:**
12+
- **Admin Portal: Apps: Manage Access: Credentials**
13+
- added option to provide external credentials for app
14+
15+
**Fixes:**
16+
- **Admin Portal:APIs:Edit / Create API**
17+
- catch error when user uploads an empty file
18+
- **Admin Portal:Manage Apps: Re-generate App Credentials**
19+
- fixed bug to use the organization setting for app credential expiry when re-generating app credentials
20+
21+
**Quickstart:**
22+
- **apim server mongo db name**
23+
- removed from configuration, hardcoded to `solace-apim-server`
24+
25+
26+
527
## Version 0.3.1
628
* [API-M Admin & Developer Portal](https://github.com/solace-iot-team/async-apim/tree/main/apim-portal): 0.3.1
729
* [API-M Server OpenAPI](https://github.com/solace-iot-team/async-apim/blob/main/apim-server/server/common/api.yml): 0.3.1

apim-portal/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "async-apim-portal",
3-
"version": "0.3.1",
3+
"version": "0.3.2",
44
"description": "Solace Async API Management Portal",
55
"repository": {
66
"type": "git",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
2+
import React from "react";
3+
4+
import { Button } from 'primereact/button';
5+
import { Toolbar } from 'primereact/toolbar';
6+
7+
import { ApiCallState, TApiCallState } from "../../../../utils/ApiCallState";
8+
import { TAPAppDisplay_Credentials } from "../../../../displayServices/APAppsDisplayService/APAppsDisplayService";
9+
import { APSClientOpenApi } from "../../../../utils/APSClientOpenApi";
10+
import APAdminPortalAppsDisplayService from "../../../displayServices/APAdminPortalAppsDisplayService";
11+
import { E_CALL_STATE_ACTIONS } from "../ManageAppsCommon";
12+
import { EditExternalCredentialsForm } from "./EditExternalCredentialsForm";
13+
14+
import '../../../../components/APComponents.css';
15+
import "../ManageApps.css";
16+
17+
export interface IEditExternalCredentialsProps {
18+
organizationId: string;
19+
apAppDisplay_Credentials: TAPAppDisplay_Credentials;
20+
onSaveSuccess: (apiCallState: TApiCallState) => void;
21+
onCancel: () => void;
22+
onError: (apiCallState: TApiCallState) => void;
23+
onLoadingChange: (isLoading: boolean) => void;
24+
}
25+
26+
export const EditExternalCredentials: React.FC<IEditExternalCredentialsProps> = (props: IEditExternalCredentialsProps) => {
27+
const ComponentName = 'EditExternalCredentials';
28+
29+
type TManagedObject = TAPAppDisplay_Credentials;
30+
31+
const FormId = `ManageApps_ManageAccess_${ComponentName}`;
32+
33+
const [managedObject, setManagedObject] = React.useState<TManagedObject>();
34+
const [apiCallStatus, setApiCallStatus] = React.useState<TApiCallState | null>(null);
35+
36+
const apiUpdateManagedObject = async(mo: TManagedObject): Promise<TApiCallState> => {
37+
const funcName = 'apiUpdateManagedObject';
38+
const logName = `${ComponentName}.${funcName}()`;
39+
let callState: TApiCallState = ApiCallState.getInitialCallState(E_CALL_STATE_ACTIONS.API_UPDATE_APP, `update app: ${mo.apEntityId.displayName}`);
40+
try {
41+
await APAdminPortalAppsDisplayService.apiUpdateExternal_ApAppDisplay_Credentials({
42+
organizationId: props.organizationId,
43+
apAppDisplay_Credentials: mo
44+
});
45+
} catch(e: any) {
46+
APSClientOpenApi.logError(logName, e);
47+
callState = ApiCallState.addErrorToApiCallState(e, callState);
48+
}
49+
setApiCallStatus(callState);
50+
return callState;
51+
}
52+
53+
const doInitialize = async () => {
54+
setManagedObject(props.apAppDisplay_Credentials);
55+
}
56+
57+
// * useEffect Hooks *
58+
59+
React.useEffect(() => {
60+
doInitialize();
61+
}, []); /* eslint-disable-line react-hooks/exhaustive-deps */
62+
63+
React.useEffect(() => {
64+
// const funcName = 'useEffect[apiCallStatus]';
65+
// const logName = `${ComponentName}.${funcName}()`;
66+
if (apiCallStatus !== null) {
67+
if(!apiCallStatus.success) props.onError(apiCallStatus);
68+
else {
69+
// if(updatedManagedObject === undefined) throw new Error(`${logName}: updatedManagedObject === undefined`);
70+
props.onSaveSuccess(apiCallStatus);
71+
}
72+
}
73+
}, [apiCallStatus]); /* eslint-disable-line react-hooks/exhaustive-deps */
74+
75+
const doSubmitManagedObject = async (mo: TManagedObject) => {
76+
props.onLoadingChange(true);
77+
await apiUpdateManagedObject(mo);
78+
props.onLoadingChange(false);
79+
// setRefreshCounter(refreshCounter + 1);
80+
}
81+
82+
const onSubmit = (mo: TManagedObject) => {
83+
doSubmitManagedObject(mo);
84+
}
85+
86+
const renderManagedObjectFormFooter = (): JSX.Element => {
87+
const managedObjectFormFooterLeftToolbarTemplate = () => {
88+
return (
89+
<React.Fragment>
90+
<Button type="button" label="Cancel" className="p-button-text p-button-plain" onClick={props.onCancel} />
91+
</React.Fragment>
92+
);
93+
}
94+
const managedObjectFormFooterRightToolbarTemplate = () => {
95+
return (
96+
<React.Fragment>
97+
<Button key={ComponentName+'Save'} form={FormId} type="submit" label="Save" icon="pi pi-save" className="p-button-text p-button-plain p-button-outlined" />
98+
</React.Fragment>
99+
);
100+
}
101+
return (
102+
<Toolbar className="p-mb-4" left={managedObjectFormFooterLeftToolbarTemplate} right={managedObjectFormFooterRightToolbarTemplate} />
103+
)
104+
}
105+
106+
const renderManagedObjectForm = (mo: TManagedObject) => {
107+
return (
108+
<div className="card p-mt-6">
109+
<div className="p-fluid">
110+
<EditExternalCredentialsForm
111+
// key={ComponentName + '_EditNewCredentialsForm_' + refreshCounter}
112+
formId={FormId}
113+
organizationId={props.organizationId}
114+
apAppDisplay_Credentials={mo}
115+
onError={props.onError}
116+
onLoadingChange={props.onLoadingChange}
117+
onSubmit={onSubmit}
118+
/>
119+
{/* footer */}
120+
{ renderManagedObjectFormFooter() }
121+
</div>
122+
</div>
123+
);
124+
}
125+
126+
127+
return (
128+
<div className="ap-manage-apps">
129+
130+
{ managedObject && renderManagedObjectForm(managedObject) }
131+
132+
</div>
133+
);
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
2+
import React from "react";
3+
import { useForm, Controller } from 'react-hook-form';
4+
5+
import { InputText } from 'primereact/inputtext';
6+
import { classNames } from 'primereact/utils';
7+
8+
import { TApiCallState } from "../../../../utils/ApiCallState";
9+
import APDisplayUtils from "../../../../displayServices/APDisplayUtils";
10+
import { APConnectorFormValidationRules } from "../../../../utils/APConnectorOpenApiFormValidationRules";
11+
import { TAPAppDisplay_Credentials } from "../../../../displayServices/APAppsDisplayService/APAppsDisplayService";
12+
13+
import '../../../../components/APComponents.css';
14+
import "../ManageApps.css";
15+
16+
export interface IEditExternalCredentialsFormProps {
17+
organizationId: string;
18+
apAppDisplay_Credentials: TAPAppDisplay_Credentials;
19+
formId: string;
20+
onSubmit: (apAppDisplay_Credentials: TAPAppDisplay_Credentials) => void;
21+
onError: (apiCallState: TApiCallState) => void;
22+
onLoadingChange: (isLoading: boolean) => void;
23+
}
24+
25+
export const EditExternalCredentialsForm: React.FC<IEditExternalCredentialsFormProps> = (props: IEditExternalCredentialsFormProps) => {
26+
// const ComponentName = 'EditExternalCredentialsForm';
27+
28+
type TManagedObject = TAPAppDisplay_Credentials;
29+
type TManagedObjectFormData = {
30+
consumerKey: string;
31+
consumerSecret: string;
32+
};
33+
type TManagedObjectFormDataEnvelope = {
34+
formData: TManagedObjectFormData;
35+
}
36+
37+
const transform_ManagedObject_To_FormDataEnvelope = (mo: TManagedObject): TManagedObjectFormDataEnvelope => {
38+
const fd: TManagedObjectFormData = {
39+
consumerKey: mo.apAppCredentials.secret.consumerKey,
40+
consumerSecret: mo.apAppCredentials.secret.consumerSecret ? mo.apAppCredentials.secret.consumerSecret : '',
41+
};
42+
return {
43+
formData: fd
44+
};
45+
}
46+
47+
const create_ManagedObject_From_FormEntities = ({formDataEnvelope}: {
48+
formDataEnvelope: TManagedObjectFormDataEnvelope;
49+
}): TManagedObject => {
50+
const mo: TManagedObject = props.apAppDisplay_Credentials;
51+
const fd: TManagedObjectFormData = formDataEnvelope.formData;
52+
mo.apAppCredentials.secret.consumerKey = fd.consumerKey;
53+
mo.apAppCredentials.secret.consumerSecret = fd.consumerSecret;
54+
return mo;
55+
}
56+
57+
const [managedObject] = React.useState<TManagedObject>(props.apAppDisplay_Credentials);
58+
const [managedObjectFormDataEnvelope, setManagedObjectFormDataEnvelope] = React.useState<TManagedObjectFormDataEnvelope>();
59+
// const [apiCallStatus, setApiCallStatus] = React.useState<TApiCallState | null>(null);
60+
const managedObjectUseForm = useForm<TManagedObjectFormDataEnvelope>();
61+
62+
const doInitialize = async () => {
63+
setManagedObjectFormDataEnvelope(transform_ManagedObject_To_FormDataEnvelope(managedObject));
64+
}
65+
66+
// * useEffect Hooks *
67+
68+
React.useEffect(() => {
69+
// alert(`${ComponentName}: mounting ...`);
70+
doInitialize();
71+
}, []); /* eslint-disable-line react-hooks/exhaustive-deps */
72+
73+
React.useEffect(() => {
74+
if(managedObjectFormDataEnvelope) {
75+
managedObjectUseForm.setValue('formData', managedObjectFormDataEnvelope.formData);
76+
}
77+
}, [managedObjectFormDataEnvelope]) /* eslint-disable-line react-hooks/exhaustive-deps */
78+
79+
// React.useEffect(() => {
80+
// if (apiCallStatus !== null) {
81+
// if(!apiCallStatus.success) props.onError(apiCallStatus);
82+
// }
83+
// }, [apiCallStatus]); /* eslint-disable-line react-hooks/exhaustive-deps */
84+
85+
const onSubmitManagedObjectForm = (newMofde: TManagedObjectFormDataEnvelope) => {
86+
props.onSubmit(create_ManagedObject_From_FormEntities({
87+
formDataEnvelope: newMofde,
88+
}));
89+
setManagedObjectFormDataEnvelope(newMofde);
90+
}
91+
92+
const onInvalidSubmitManagedObjectForm = () => {
93+
// placeholder
94+
}
95+
96+
const renderManagedObjectForm = () => {
97+
return (
98+
<div className="card p-mt-4">
99+
<div className="p-fluid">
100+
<form id={props.formId} onSubmit={managedObjectUseForm.handleSubmit(onSubmitManagedObjectForm, onInvalidSubmitManagedObjectForm)} className="p-fluid">
101+
{/* consumerKey */}
102+
<div className="p-field">
103+
<span className="p-float-label">
104+
<Controller
105+
control={managedObjectUseForm.control}
106+
name="formData.consumerKey"
107+
rules={APConnectorFormValidationRules.ConsumerKey()}
108+
render={( { field, fieldState }) => {
109+
return(
110+
<InputText
111+
id={field.name}
112+
{...field}
113+
autoFocus={true}
114+
className={classNames({ 'p-invalid': fieldState.invalid })}
115+
/>
116+
)}}
117+
/>
118+
<label className={classNames({ 'p-error': managedObjectUseForm.formState.errors.formData?.consumerKey })}>Consumer Key*</label>
119+
</span>
120+
{APDisplayUtils.displayFormFieldErrorMessage(managedObjectUseForm.formState.errors.formData?.consumerKey)}
121+
</div>
122+
{/* consumerSecret */}
123+
<div className="p-field">
124+
<span className="p-float-label">
125+
<Controller
126+
control={managedObjectUseForm.control}
127+
name="formData.consumerSecret"
128+
rules={APConnectorFormValidationRules.ConsumerSecret()}
129+
render={( { field, fieldState }) => {
130+
// console.log(`field=${field.name}, fieldState=${JSON.stringify(fieldState)}`);
131+
return(
132+
<InputText
133+
id={field.name}
134+
{...field}
135+
autoFocus={false}
136+
className={classNames({ 'p-invalid': fieldState.invalid })}
137+
/>
138+
)}}
139+
/>
140+
<label className={classNames({ 'p-error': managedObjectUseForm.formState.errors.formData?.consumerSecret })}>Consumer Secret*</label>
141+
</span>
142+
{APDisplayUtils.displayFormFieldErrorMessage(managedObjectUseForm.formState.errors.formData?.consumerSecret)}
143+
</div>
144+
</form>
145+
</div>
146+
</div>
147+
);
148+
}
149+
150+
151+
return (
152+
<div className="ap-manage-apps">
153+
154+
{ renderManagedObjectForm() }
155+
156+
</div>
157+
);
158+
}

apim-portal/src/admin-portal/components/ManageApps/ManageAccess/EditCredentials.tsx renamed to apim-portal/src/admin-portal/components/ManageApps/ManageAccess/EditInternalCredentials.tsx

+9-13
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,38 @@ import { Toolbar } from 'primereact/toolbar';
77
import { ApiCallState, TApiCallState } from "../../../../utils/ApiCallState";
88
import { TAPAppDisplay_Credentials } from "../../../../displayServices/APAppsDisplayService/APAppsDisplayService";
99
import { APSClientOpenApi } from "../../../../utils/APSClientOpenApi";
10-
import APAdminPortalAppsDisplayService, { TAPAdminPortalAppDisplay } from "../../../displayServices/APAdminPortalAppsDisplayService";
10+
import APAdminPortalAppsDisplayService from "../../../displayServices/APAdminPortalAppsDisplayService";
1111
import { EAction, E_CALL_STATE_ACTIONS } from "../ManageAppsCommon";
12-
import { EditNewCredentialsForm } from "./EditNewCredentialsForm";
12+
import { EditInternalCredentialsForm } from "./EditInternalCredentialsForm";
1313

1414
import '../../../../components/APComponents.css';
1515
import "../ManageApps.css";
1616

17-
export interface IEditCredentialsProps {
17+
export interface IEditInternalCredentialsProps {
1818
organizationId: string;
19-
apAdminPortalAppDisplay: TAPAdminPortalAppDisplay;
19+
apAppDisplay_Credentials: TAPAppDisplay_Credentials;
2020
onSaveSuccess: (apiCallState: TApiCallState) => void;
2121
onCancel: () => void;
2222
onError: (apiCallState: TApiCallState) => void;
2323
onLoadingChange: (isLoading: boolean) => void;
2424
}
2525

26-
export const EditCredentials: React.FC<IEditCredentialsProps> = (props: IEditCredentialsProps) => {
27-
const ComponentName = 'EditCredentials';
26+
export const EditInternalCredentials: React.FC<IEditInternalCredentialsProps> = (props: IEditInternalCredentialsProps) => {
27+
const ComponentName = 'EditInternalCredentials';
2828

2929
type TManagedObject = TAPAppDisplay_Credentials;
3030

3131
const FormId = `ManageApps_ManageAccess_${ComponentName}`;
3232

3333
const [managedObject, setManagedObject] = React.useState<TManagedObject>();
34-
// const [updatedManagedObject, setUpdatedManagedObject] = React.useState<TManagedObject>();
3534
const [apiCallStatus, setApiCallStatus] = React.useState<TApiCallState | null>(null);
36-
// const [refreshCounter, setRefreshCounter] = React.useState<number>(0);
3735

3836
const apiUpdateManagedObject = async(mo: TManagedObject): Promise<TApiCallState> => {
3937
const funcName = 'apiUpdateManagedObject';
4038
const logName = `${ComponentName}.${funcName}()`;
4139
let callState: TApiCallState = ApiCallState.getInitialCallState(E_CALL_STATE_ACTIONS.API_UPDATE_APP, `update app: ${mo.apEntityId.displayName}`);
4240
try {
43-
await APAdminPortalAppsDisplayService.apiUpdate_ApAppDisplay_Credentials({
41+
await APAdminPortalAppsDisplayService.apiUpdateInternal_ApAppDisplay_Credentials({
4442
organizationId: props.organizationId,
4543
apAppDisplay_Credentials: mo
4644
});
@@ -53,9 +51,7 @@ export const EditCredentials: React.FC<IEditCredentialsProps> = (props: IEditCre
5351
}
5452

5553
const doInitialize = async () => {
56-
setManagedObject(APAdminPortalAppsDisplayService.get_ApAppDisplay_Credentials({
57-
apAppDisplay: props.apAdminPortalAppDisplay
58-
}));
54+
setManagedObject(props.apAppDisplay_Credentials);
5955
}
6056

6157
// * useEffect Hooks *
@@ -111,7 +107,7 @@ export const EditCredentials: React.FC<IEditCredentialsProps> = (props: IEditCre
111107
return (
112108
<div className="card p-mt-6">
113109
<div className="p-fluid">
114-
<EditNewCredentialsForm
110+
<EditInternalCredentialsForm
115111
// key={ComponentName + '_EditNewCredentialsForm_' + refreshCounter}
116112
formId={FormId}
117113
organizationId={props.organizationId}

0 commit comments

Comments
 (0)