-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: generate API Credentials in Admin Portal (#1027)
* feat: adding api credential tab * feat: add zero state card under api credentails tab * feat: add a new tab * fix: make lms-service run as expected * fix: fix coupon.test.jsx lint error * feat: generate API Credentials Tab in Admin Portal * fix: modify modal * fix: modify lmsservice url * fix: remove dependency in useffect * fix: add api-document url * fix: lots of little fixes * fix: test fixes * fix: more changes * fix: more fixes * fix: PR review requests
- Loading branch information
Showing
24 changed files
with
924 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from 'react'; | ||
import { Hyperlink } from '@edx/paragon'; | ||
import PropTypes from 'prop-types'; | ||
|
||
const HelpCenterButton = ({ | ||
url, | ||
children, | ||
...rest | ||
}) => { | ||
const destinationUrl = url; | ||
|
||
return ( | ||
<Hyperlink | ||
{...rest} | ||
target="_blank" | ||
className="btn btn-outline-primary side-button" | ||
destination={destinationUrl} | ||
> | ||
{children} | ||
</Hyperlink> | ||
); | ||
}; | ||
|
||
HelpCenterButton.defaultProps = { | ||
children: 'Help Center', | ||
}; | ||
|
||
HelpCenterButton.propTypes = { | ||
children: PropTypes.node, | ||
url: PropTypes.string, | ||
}; | ||
|
||
export default HelpCenterButton; |
98 changes: 98 additions & 0 deletions
98
src/components/settings/SettingsApiCredentialsTab/APICredentialsPage.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import React, { useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { Form, Hyperlink } from '@edx/paragon'; | ||
import { dataPropType } from './constants'; | ||
import RegenerateCredentialWarningModal from './RegenerateCredentialWarningModal'; | ||
import CopyButton from './CopyButton'; | ||
import { API_CLIENT_DOCUMENTATION, HELP_CENTER_LINK } from '../data/constants'; | ||
|
||
const APICredentialsPage = ({ data, setData }) => { | ||
const [formValue, setFormValue] = useState(''); | ||
const handleFormChange = (e) => { | ||
setFormValue(e.target.value); | ||
}; | ||
return ( | ||
<div> | ||
<div className="mb-4"> | ||
<h3>Your API credentials</h3> | ||
<p> | ||
Copy and paste the following credential information and send it to your API developer(s). | ||
</p> | ||
</div> | ||
<div className="mb-4 api-cred-fields"> | ||
<h4> | ||
Application name: | ||
<span>{data?.name}</span> | ||
</h4> | ||
<h4> | ||
Allowed URIs: | ||
<span>{data?.redirect_uris}</span> | ||
</h4> | ||
<h4> | ||
API client ID: | ||
<span>{data?.client_id}</span> | ||
</h4> | ||
<h4> | ||
API client secret: | ||
<span>{data?.client_secret}</span> | ||
</h4> | ||
<h4>API client documentation: | ||
<span>{API_CLIENT_DOCUMENTATION}</span> | ||
</h4> | ||
<h4> | ||
Last generated on: | ||
<span>{data?.updated}</span> | ||
</h4> | ||
<div className="my-3"> | ||
<CopyButton data={data} /> | ||
</div> | ||
</div> | ||
<div className="mb-4"> | ||
<h3>Redirect URIs (optional)</h3> | ||
<p> | ||
If you need additional redirect URIs, add them below and regenerate your API credentials. | ||
You will need to communicate the new credentials to your API developers. | ||
</p> | ||
<Form.Control | ||
value={formValue} | ||
onChange={handleFormChange} | ||
floatingLabel="Redirect URIs" | ||
data-testid="form-control" | ||
/> | ||
<p> | ||
Allowed URIs list, space separated | ||
</p> | ||
<RegenerateCredentialWarningModal | ||
redirectURIs={formValue} | ||
data={data} | ||
setData={setData} | ||
/> | ||
</div> | ||
<div className="mb-4"> | ||
<h3>Questions or modifications?</h3> | ||
<p> | ||
To troubleshoot your API credentialing, or to request additional API endpoints to your | ||
credentials, | ||
<Hyperlink | ||
variant="muted" | ||
destination={HELP_CENTER_LINK} | ||
> | ||
contact Enterprise Customer Support. | ||
</Hyperlink> | ||
</p> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
APICredentialsPage.defaultProps = { | ||
data: null, | ||
}; | ||
|
||
APICredentialsPage.propTypes = { | ||
data: PropTypes.shape(dataPropType), | ||
setData: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default APICredentialsPage; |
5 changes: 5 additions & 0 deletions
5
src/components/settings/SettingsApiCredentialsTab/Context.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { createContext } from 'react'; | ||
|
||
export const ErrorContext = createContext(null); | ||
export const ShowSuccessToast = createContext(null); | ||
export const EnterpriseId = createContext(null); |
13 changes: 13 additions & 0 deletions
13
src/components/settings/SettingsApiCredentialsTab/CopiedToast.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Toast } from '@edx/paragon'; | ||
|
||
const CopiedToast = ({ content, ...rest }) => ( | ||
<Toast {...rest}> | ||
{content} | ||
</Toast> | ||
); | ||
CopiedToast.propTypes = { | ||
content: PropTypes.string.isRequired, | ||
}; | ||
export default CopiedToast; |
48 changes: 48 additions & 0 deletions
48
src/components/settings/SettingsApiCredentialsTab/CopyButton.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React, { useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { Button } from '@edx/paragon'; | ||
import { ContentCopy } from '@edx/paragon/icons'; | ||
import CopiedToast from './CopiedToast'; | ||
import { dataPropType } from './constants'; | ||
|
||
const CopyButton = ({ data }) => { | ||
const [isCopyLinkToastOpen, setIsCopyLinkToastOpen] = useState(false); | ||
const [copiedError, setCopiedError] = useState(false); | ||
|
||
const handleCopyLink = async () => { | ||
try { | ||
const jsonString = JSON.stringify(data); | ||
await navigator.clipboard.writeText(jsonString); | ||
} catch (error) { | ||
setCopiedError(true); | ||
} finally { | ||
setIsCopyLinkToastOpen(true); | ||
} | ||
}; | ||
const handleCloseLinkCopyToast = () => { | ||
setIsCopyLinkToastOpen(false); | ||
}; | ||
return ( | ||
<> | ||
<Button | ||
variant="primary" | ||
iconAfter={ContentCopy} | ||
onClick={handleCopyLink} | ||
> | ||
Copy credentials to clipboard | ||
</Button> | ||
<CopiedToast | ||
content={copiedError ? 'Cannot copied to the clipboard' : 'Copied Successfully'} | ||
show={isCopyLinkToastOpen} | ||
onClose={handleCloseLinkCopyToast} | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
CopyButton.propTypes = { | ||
data: PropTypes.shape(dataPropType), | ||
}; | ||
|
||
export default CopyButton; |
16 changes: 16 additions & 0 deletions
16
src/components/settings/SettingsApiCredentialsTab/FailedAlert.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Alert } from '@edx/paragon'; | ||
import { Error } from '@edx/paragon/icons'; | ||
import { credentialErrorMessage } from './constants'; | ||
|
||
const FailedAlert = () => ( | ||
<Alert variant="danger" icon={Error}> | ||
<Alert.Heading> | ||
Credential generation failed | ||
</Alert.Heading> | ||
<p> | ||
{credentialErrorMessage} | ||
</p> | ||
</Alert> | ||
); | ||
|
||
export default FailedAlert; |
98 changes: 98 additions & 0 deletions
98
src/components/settings/SettingsApiCredentialsTab/RegenerateCredentialWarningModal.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import React, { useContext } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { | ||
ActionRow, Button, Icon, ModalDialog, useToggle, | ||
} from '@edx/paragon'; | ||
import { Warning } from '@edx/paragon/icons'; | ||
|
||
import { | ||
ErrorContext, | ||
ShowSuccessToast, EnterpriseId, | ||
} from './Context'; | ||
import LmsApiService from '../../../data/services/LmsApiService'; | ||
import { dataPropType } from './constants'; | ||
|
||
const RegenerateCredentialWarningModal = ({ | ||
redirectURIs, | ||
data, | ||
setData, | ||
}) => { | ||
const [isOn, setOn, setOff] = useToggle(false); | ||
const [, setHasError] = useContext(ErrorContext); | ||
const [, setShowSuccessToast] = useContext(ShowSuccessToast); | ||
const enterpriseId = useContext(EnterpriseId); | ||
const handleOnClickRegeneration = async () => { | ||
try { | ||
const response = await LmsApiService.regenerateAPICredentials(redirectURIs, enterpriseId); | ||
const newURIs = response.data.redirect_uris; | ||
setShowSuccessToast(true); | ||
const updatedData = data; | ||
updatedData.redirect_uris = newURIs; | ||
setData(updatedData); | ||
} catch (error) { | ||
setHasError(true); | ||
} finally { | ||
setOff(true); | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<Button | ||
variant="primary" | ||
onClick={setOn} | ||
className="mb-2 mb-sm-0" | ||
> | ||
Regenerate API Credentials | ||
</Button> | ||
<ModalDialog | ||
title="Warning Message" | ||
size="md" | ||
isOpen={isOn} | ||
onClose={setOff} | ||
hasCloseButton | ||
isFullscreenOnMobile | ||
isFullscreenScroll | ||
> | ||
<ModalDialog.Header> | ||
<ModalDialog.Title> | ||
<div className="d-flex"> | ||
<Icon src={Warning} className="warning-icon mr-2 align-items-baseline-center" /> | ||
Regenerate API credentials? | ||
</div> | ||
</ModalDialog.Title> | ||
</ModalDialog.Header> | ||
<ModalDialog.Body> | ||
<p> | ||
Any system, job, or script using the previous credentials will no | ||
longer be able to authenticate with the edX API. | ||
</p> | ||
<p> | ||
If you do regenerate, you will need to send the new credentials to your developers. | ||
</p> | ||
</ModalDialog.Body> | ||
<ModalDialog.Footer> | ||
<ActionRow> | ||
<ModalDialog.CloseButton variant="tertiary"> | ||
Cancel | ||
</ModalDialog.CloseButton> | ||
<Button | ||
variant="primary" | ||
onClick={handleOnClickRegeneration} | ||
> | ||
Regenerate | ||
</Button> | ||
</ActionRow> | ||
</ModalDialog.Footer> | ||
</ModalDialog> | ||
</> | ||
); | ||
}; | ||
|
||
RegenerateCredentialWarningModal.propTypes = { | ||
redirectURIs: PropTypes.string.isRequired, | ||
data: PropTypes.shape(dataPropType), | ||
setData: PropTypes.func.isRequired, | ||
}; | ||
|
||
export default RegenerateCredentialWarningModal; |
Oops, something went wrong.