Skip to content

Commit

Permalink
Merge branch 'master' into DHIS2-10247/allow-list
Browse files Browse the repository at this point in the history
  • Loading branch information
tomzemp committed Feb 14, 2024
2 parents f1fefc5 + 854a972 commit 27df12a
Show file tree
Hide file tree
Showing 16 changed files with 611 additions and 12 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## [29.16.2](https://github.com/dhis2/settings-app/compare/v29.16.1...v29.16.2) (2024-02-12)


### Bug Fixes

* add back OAUTH2 [DHIS2-15326] ([#1300](https://github.com/dhis2/settings-app/issues/1300)) ([520b1c7](https://github.com/dhis2/settings-app/commit/520b1c76fd8705eb311813c1bea0a9277aef8759))

## [29.16.1](https://github.com/dhis2/settings-app/compare/v29.16.0...v29.16.1) (2024-01-24)


### Bug Fixes

* remove keyAnalyticsMaintenanceMode [DHIS2-16534] ([#1296](https://github.com/dhis2/settings-app/issues/1296)) ([d9fdbc8](https://github.com/dhis2/settings-app/commit/d9fdbc8a502c0636353df368f26ebb9459e32e00))

# [29.16.0](https://github.com/dhis2/settings-app/compare/v29.15.9...v29.16.0) (2024-01-23)


### Features

* add in scheduling settings to settings app [DHIS2-15765] ([#1295](https://github.com/dhis2/settings-app/issues/1295)) ([ba6f2a1](https://github.com/dhis2/settings-app/commit/ba6f2a17c364b16c967a9f548ed718163179b58b))

## [29.15.9](https://github.com/dhis2/settings-app/compare/v29.15.8...v29.15.9) (2023-12-14)


Expand Down
18 changes: 18 additions & 0 deletions cypress/integration/oauth2.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Feature: Users should be able to add OAuth2 clients

Scenario: User adds OAuth2 client
Given the user visits the 'OAuth2 Clients' page
And the user clicks on the 'Add OAuth2 client' button
And the user enters the relevant details in the form that appears
Then a snackbar message should appear telling the user that the client was saved
And the page should show the new client in the table of clients

Scenario: No OAuth2 clients are present
Given the user visits the 'OAuth2 Clients' page
And there are no OAuth2 clients
Then the message 'There are currently no OAuth2 clients registered' should be shown

Scenario: Some OAuth2 clients are present
Given the user visits the 'OAuth2 Clients' page
And there are some OAuth2 clients
Then a table showing all clients should be present
1 change: 1 addition & 0 deletions d2.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const config = {
name: 'settings',
title: 'Settings',
coreApp: true,
minDHIS2Version: '2.41',

entryPoints: {
app: './src/App.js',
Expand Down
33 changes: 28 additions & 5 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2023-12-15T15:25:33.200Z\n"
"PO-Revision-Date: 2023-12-15T15:25:33.200Z\n"
"POT-Creation-Date: 2024-02-09T16:13:02.251Z\n"
"PO-Revision-Date: 2024-02-09T16:13:02.251Z\n"

msgid "Failed to load: {{error}}"
msgstr "Failed to load: {{error}}"
Expand Down Expand Up @@ -146,6 +146,12 @@ msgstr "Synchronization"
msgid "Synchronization settings"
msgstr "Synchronization settings"

msgid "OAuth2 Clients"
msgstr "OAuth2 Clients"

msgid "Scheduled jobs"
msgstr "Scheduled jobs"

msgid "This field is required"
msgstr "This field is required"

Expand Down Expand Up @@ -461,9 +467,6 @@ msgstr "Last 9 years"
msgid "Respect category option start and end date in analytics table export"
msgstr "Respect category option start and end date in analytics table export"

msgid "Put analytics in maintenance mode"
msgstr "Put analytics in maintenance mode"

msgid "Include zero data values in analytics tables"
msgstr "Include zero data values in analytics tables"

Expand Down Expand Up @@ -751,3 +754,23 @@ msgstr "Remote server username"

msgid "Remote server password"
msgstr "Remote server password"

msgid "Number of minutes after which a stale job is reset to scheduled state (1-60)"
msgstr "Number of minutes after which a stale job is reset to scheduled state (1-60)"

msgid "Number of minutes after which a completed ad-hoc job is deleted (1+)"
msgstr "Number of minutes after which a completed ad-hoc job is deleted (1+)"

msgid ""
"Maximum number of hours a job may trigger after its intended time if job "
"has not yet run (1-24)"
msgstr ""
"Maximum number of hours a job may trigger after its intended time if job "
"has not yet run (1-24)"

msgid ""
"Job execution interval (seconds) below which a job will be logged at debug "
"(rather than info) level (20+)"
msgstr ""
"Job execution interval (seconds) below which a job will be logged at debug "
"(rather than info) level (20+)"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "settings-app",
"version": "29.15.9",
"version": "29.16.2",
"description": "",
"license": "BSD-3-Clause",
"private": true,
Expand Down
162 changes: 162 additions & 0 deletions src/oauth2-client-editor/ClientForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import i18n from '@dhis2/d2-i18n'
import { Button, Modal, ModalTitle, ModalContent } from '@dhis2/ui'
import { getInstance as getD2 } from 'd2'
import FormBuilder from 'd2-ui/lib/forms/FormBuilder.component.js'
import { isUrlArray, isRequired } from 'd2-ui/lib/forms/Validators.js'
import PropTypes from 'prop-types'
import React from 'react'
import MultiToggle from '../form-fields/multi-toggle.js'
import TextField from '../form-fields/text-field.js'
import styles from './ClientForm.module.css'

const formFieldStyle = {
width: '100%',
}

const validateClientID = async (v) => {
const d2 = await getD2()
const list = await d2.models.oAuth2Clients.list({
paging: false,
filter: [`cid:eq:${v}`],
})
if (list.size > 0) {
throw i18n.t('This client ID is already taken')
}
}

const ClientForm = ({ clientModel, onUpdate, onSave, onCancel }) => {
const grantTypes = ((clientModel && clientModel.grantTypes) || []).reduce(
(curr, prev) => {
curr[prev] = true
return curr
},
{}
)

const fields = [
{
name: 'name',
value: clientModel.name,
component: TextField,
props: {
floatingLabelText: i18n.t('Name'),
style: formFieldStyle,
changeEvent: 'onBlur',
},
validators: [
{
validator: isRequired,
message: i18n.t('Required'),
},
],
},
{
name: 'cid',
value: clientModel.cid,
component: TextField,
props: {
floatingLabelText: i18n.t('Client ID'),
style: formFieldStyle,
changeEvent: 'onBlur',
},
validators: [
{
validator: isRequired,
message: i18n.t('Required'),
},
{
validator: (v) => v.toString().trim().length > 0,
message: i18n.t('Required'),
},
],
asyncValidators: [validateClientID],
},
{
name: 'secret',
value: clientModel && clientModel.secret,
component: TextField,
props: {
floatingLabelText: i18n.t('Client Secret'),
disabled: true,
style: formFieldStyle,
},
},
{
name: 'grantTypes',
component: MultiToggle,
style: formFieldStyle,
props: {
label: i18n.t('Grant Types'),
items: [
{
name: 'password',
text: i18n.t('Password'),
value: grantTypes.password,
},
{
name: 'refresh_token',
text: i18n.t('Refresh token'),
value: grantTypes.refresh_token,
},
{
name: 'authorization_code',
text: i18n.t('Authorization code'),
value: grantTypes.authorization_code,
},
],
},
},
{
name: 'redirectUris',
value: (clientModel.redirectUris || []).join('\n'),
component: TextField,
props: {
hintText: i18n.t('One URL per line'),
floatingLabelText: i18n.t('Redirect URIs'),
multiLine: true,
style: formFieldStyle,
changeEvent: 'onBlur',
},
validators: [
{
validator: isUrlArray,
message: i18n.t('This field should contain a list of URLs'),
},
],
},
]

const headerText =
clientModel.id === undefined
? i18n.t('Create new OAuth2 Client')
: i18n.t('Edit OAuth2 Client')
return (
<Modal onClose={onCancel}>
<ModalTitle>{headerText}</ModalTitle>
<ModalContent>
<FormBuilder fields={fields} onUpdateField={onUpdate} />
<div style={{ marginTop: '1rem' }}>
<Button primary onClick={onSave}>
{i18n.t('Save')}
</Button>
<Button
secondary
onClick={onCancel}
className={styles.cancelBtn}
>
{i18n.t('Cancel')}
</Button>
</div>
</ModalContent>
</Modal>
)
}

ClientForm.propTypes = {
clientModel: PropTypes.object.isRequired,
onCancel: PropTypes.func.isRequired,
onSave: PropTypes.func.isRequired,
onUpdate: PropTypes.func.isRequired,
}

export default ClientForm
3 changes: 3 additions & 0 deletions src/oauth2-client-editor/ClientForm.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.cancelBtn {
float: right;
}
78 changes: 78 additions & 0 deletions src/oauth2-client-editor/ClientsList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import i18n from '@dhis2/d2-i18n'
import {
CenteredContent,
Table,
TableBody,
TableCell,
TableCellHead,
TableHead,
TableRow,
TableRowHead,
Button,
} from '@dhis2/ui'
import PropTypes from 'prop-types'
import React from 'react'
import styles from './ClientsList.module.css'

const ClientsList = ({ clients, onClientEdit, onClientDelete }) => {
if (clients.length === 0) {
return (
<CenteredContent>
<p>
{i18n.t('There are currently no OAuth2 clients registered')}
</p>
</CenteredContent>
)
}

return (
<Table>
<TableHead>
<TableRowHead>
<TableCellHead>{i18n.t('Name')}</TableCellHead>
<TableCellHead>{i18n.t('Password')}</TableCellHead>
<TableCellHead>{i18n.t('Refresh token')}</TableCellHead>
<TableCellHead>
{i18n.t('Authorization code')}
</TableCellHead>
<TableCellHead>{/* Buttons column */}</TableCellHead>
</TableRowHead>
</TableHead>
<TableBody>
{clients.map((client) => (
<TableRow key={client.authorization_code}>
<TableCell>{client.name}</TableCell>
<TableCell>{client.password}</TableCell>
<TableCell>{client.refresh_token}</TableCell>
<TableCell>{client.authorization_code}</TableCell>
<TableCell>
<Button
small
primary
className={styles.editBtn}
onClick={() => onClientEdit(client)}
>
{i18n.t('Edit')}
</Button>
<Button
small
destructive
onClick={() => onClientDelete(client)}
>
{i18n.t('Delete')}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}

ClientsList.propTypes = {
clients: PropTypes.array.isRequired,
onClientDelete: PropTypes.func.isRequired,
onClientEdit: PropTypes.func.isRequired,
}

export default ClientsList
3 changes: 3 additions & 0 deletions src/oauth2-client-editor/ClientsList.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.editBtn {
margin-right: var(--spacers-dp16);
}
Loading

0 comments on commit 27df12a

Please sign in to comment.