Unverified Commit 4a66ad93 authored by torgiren's avatar torgiren
Browse files

add support for adding and listing emails - closes #367

Signed-off-by: torgiren's avatarMarcin Fabrykowski <git@fabrykowski.pl>
1 merge request!44add support for adding and listing emails - closes #367
Pipeline #2746 passed with stages
in 4 minutes and 10 seconds
Showing with 383 additions and 0 deletions
+383 -0
......@@ -13,6 +13,7 @@
"eslint": "^8.14.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-react": "^7.29.4",
"history": "^5.3.0",
"jest": "^27.5.1",
"jest-junit": "^13.0.0",
"nock": "^13.2.4",
......
import React from 'react'
import { Container } from 'react-bootstrap'
import AppEmailAdd from './appEmailAdd'
import AppEmailList from './appEmailList'
import PropTypes from 'prop-types'
function AppEmail (props) {
return (
<React.Fragment>
<Container>
<h1>Emails</h1>
</Container>
<Container className='my-3'>
<AppEmailAdd org={props.org} />
</Container>
<Container className='my-3'>
<AppEmailList org={props.org} />
</Container>
</React.Fragment>
)
}
AppEmail.propTypes = {
org: PropTypes.string.isRequired
}
export default AppEmail
import React, { useState } from 'react'
import { Form, Button } from 'react-bootstrap'
import axios from 'axios'
import PropTypes from 'prop-types'
function AppEmailAdd (props) {
const [input, setInputs] = useState({})
const [message, setMessage] = useState('')
const handleSubmit = (event) => {
event.preventDefault()
const fields = [['mail', 'Mail'], ['cn', 'Name'], ['sn', 'Surname']]
for (let i = 0; i < fields.length; i++) {
console.log(input[fields[i][0]])
if (input[fields[i][0]] == null || input[fields[i][0]] === '') {
// alert("Field "+fields[i][1]+" cannot be empty");
setMessage('no field ' + fields[i][1])
return 2
}
}
const values = `mail: "${input.mail}",
org: "${props.org}",
cn: "${input.cn}",
sn: "${input.sn}"`
const query = JSON.stringify({
query: `mutation {
emailCreate(${values})
{
email {
mail,
sn,
cn,
aliases,
password
},
error
}
}`
})
const requestOptions = {
url: window.API_URL + '/graphql',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: query,
responseType: 'json'
}
axios
.request(requestOptions)
.then(function (response) {
const res = response.data // Response received from the API
if (res.data.emailCreate.error &&
res.data.emailCreate.error.includes('already exists')) {
setMessage('error 1: already exists')
return 1
}
if (res.data.emailCreate.error) {
setMessage('error 2: submit failed')
return 2
}
setMessage('added. password: ' + res.data.emailCreate.email.password)
return 0
})
.catch(function (err) {
console.log(err)
setMessage('error 3: submit error')
// alert("Submit failed")
})
setMessage('adding...')
}
const handleChange = (event) => {
const name = event.target.name
const value = event.target.value
setInputs(values => ({ ...values, [name]: value }))
}
return (
<React.Fragment>
<div data-testid='email-add-message'>{ message }</div>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="mail">
<Form.Label>Email:</Form.Label>
<Form.Control data-testid="email-add-mail" name="mail" onChange={handleChange}/>
</Form.Group>
<Form.Group controlId="cn">
<Form.Label>Name:</Form.Label>
<Form.Control data-testid="email-add-cn" name="cn" onChange={handleChange}/>
</Form.Group>
<Form.Group controlId="sn">
<Form.Label>Surname:</Form.Label>
<Form.Control data-testid="email-add-sn" name="sn" onChange={handleChange}/>
</Form.Group>
<Form.Group controlId="aliases">
<Form.Label>Aliases:</Form.Label>
<Form.Control data-testid="email-add-aliases" name="aliases" onChange={handleChange}/>
</Form.Group>
<input data-testid="email-add-org" name="org" type="hidden" value={props.org}/>
<Button data-testid="email-add-submit" type="submit" className='my-3'>Create</Button>
</Form>
</React.Fragment>
)
}
AppEmailAdd.propTypes = {
org: PropTypes.string.isRequired
}
export default AppEmailAdd
import React from 'react'
import { render, screen, waitFor, fireEvent } from '@testing-library/react'
import AppEmailAdd from './appEmailAdd'
import nock from 'nock'
test('show apps in console page', () => {
render(<AppEmailAdd org="test-org"/>)
expect(screen.queryByText(/Email:/)).toBeInTheDocument()
expect(screen.queryByText(/Name:/)).toBeInTheDocument()
expect(screen.queryByText(/Surname:/)).toBeInTheDocument()
})
test('submit email app wrong response', async () => {
window.API_URL = 'http://localhost:8080'
nock('http://localhost:8080')
.post('/graphql')
.reply(400, {
}, {
'Access-Control-Allow-Origin': '*',
'Content-type': 'application/json'
})
render(<AppEmailAdd org="test-org"/>)
fireEvent.change(screen.getByTestId('email-add-mail'), { target: { value: 'test-aa' } })
fireEvent.change(screen.getByTestId('email-add-cn'), { target: { value: 'test-aa' } })
fireEvent.change(screen.getByTestId('email-add-sn'), { target: { value: 'test-aa' } })
fireEvent.click(screen.getByText('Create'))
await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('submit error'))
})
test('submit email', async () => {
window.API_URL = 'http://localhost:8080'
nock('http://localhost:8080')
.post('/graphql')
.reply(200, {
data: {
emailCreate: {
email: {
mail: 'aa@aa.com',
cn: 'John',
sn: 'Brown',
password: 'pass123'
},
error: ''
}
}
}, {
'Access-Control-Allow-Origin': '*',
'Content-type': 'application/json'
})
render(<AppEmailAdd org="qwe"/>)
expect(screen.getByTestId('email-add-org')).toHaveDisplayValue('qwe')
fireEvent.change(screen.getByTestId('email-add-mail'), { target: { value: 'test-aa' } })
fireEvent.change(screen.getByTestId('email-add-cn'), { target: { value: 'test-aa' } })
fireEvent.change(screen.getByTestId('email-add-sn'), { target: { value: 'test-aa' } })
fireEvent.click(screen.getByText('Create'))
await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('added'))
await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('pass123'))
})
// test('submit app duplicate', async () => {
// window.API_URL = 'http://localhost:8080'
// nock('http://localhost:8080')
// .post('/graphql')
// .reply(200, {
// data: {
// emailRegister: {
// error: 'App already exists'
// }
// }
// }, {
// 'Access-Control-Allow-Origin': '*',
// 'Content-type': 'application/json'
// })
// render(<AppEmailAdd org="test-org"/>)
// fireEvent.change(screen.getByTestId('email-add-name'), { target: { value: 'test-aa' } })
// fireEvent.click(screen.getByText('Create'))
// await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('already exists'))
// })
//
test('submit email no name', async () => {
render(<AppEmailAdd org="test-org"/>)
fireEvent.click(screen.getByText('Create'))
await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('no field Mail'))
fireEvent.change(screen.getByTestId('email-add-mail'), { target: { value: 'test-aa' } })
fireEvent.click(screen.getByText('Create'))
await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('no field Name'))
fireEvent.change(screen.getByTestId('email-add-cn'), { target: { value: 'test-aa' } })
fireEvent.click(screen.getByText('Create'))
await waitFor(() => expect(screen.getByTestId('email-add-message')).toHaveTextContent('no field Surname'))
})
import React, { useState, useEffect } from 'react'
import { Table } from 'react-bootstrap'
import axios from 'axios'
import PropTypes from 'prop-types'
function AppEmailList (props) {
const [emails, setEmails] = useState(<tr><td>0</td><td colSpan="4">Loading</td></tr>)
function makeTable (content) {
setEmails(content.map((email, index) => (
<tr key={index}>
<td>{ index }</td>
<td>{ email.mail }</td>
<td>{ email.cn }</td>
<td>{ email.sn }</td>
<td><ul>{ email.aliases.map(mail => (<li key={mail}>{mail}</li>)) } </ul></td>
</tr>
)))
}
function loadEmailsMock () {
const emails = [
{
mail: 'aa@example.com',
cn: 'Jan',
sn: 'Barski',
aliases: [
'aa@example.com',
'bb@example.com'
]
},
{
mail: 'john@snow.com',
cn: 'John',
sn: 'Snow',
aliases: [
'john@snow.com',
'winter@iscomming.com'
]
}
]
makeTable(emails)
}
function loadEmails () {
const query = JSON.stringify({
query: `{
email(org: "${props.org}") {
emails {
mail,
cn,
sn,
aliases
},
error
}
}`
})
const requestOptions = {
url: window.API_URL + '/graphql',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: query,
responseType: 'json'
}
axios
.request(requestOptions)
.then(function (response) {
const res = response.data // Response received from the API
makeTable(res.data.email.emails)
return 0
})
.catch(function (err) {
console.log(err)
// alert("Submit failed")
})
}
useEffect(() => {
if (process.env.REACT_APP_TESTING) {
loadEmailsMock()
} else {
loadEmails()
}
}, [props.org])
return (
<React.Fragment>
<Table striped bordered hover>
<thead><tr>
<th>Id</th>
<th>Name</th>
<th>Name</th>
<th>Surname</th>
<th>Aliases</th>
</tr></thead>
<tbody>
{emails}
</tbody>
</Table>
</React.Fragment>
)
}
AppEmailList.propTypes = {
org: PropTypes.string.isRequired
}
export default AppEmailList
import React from 'react'
import { render, screen, waitFor } from '@testing-library/react'
import AppEmailList from './appEmailList'
import nock from 'nock'
test('list emails', async () => {
nock('http://localhost:8080')
.post('/graphql')
.reply(200, {
data: {
email: {
emails: [
{
mail: 'aa@example.com',
cn: 'Jan',
sn: 'Barski',
aliases: [
'aa@example.com',
'bb@example.com'
]
},
{
mail: 'john@snow.com',
cn: 'John',
sn: 'Snow',
aliases: [
'john@snow.com',
'winter@iscomming.com'
]
}
]
}
}
}, {
'Access-Control-Allow-Origin': '*',
'Content-type': 'application/json'
})
window.API_URL = 'http://localhost:8080'
render(<AppEmailList org='test-org'/>)
await waitFor(() => expect(screen.getAllByText('aa@example.com')).toBeInTheDocument)
await waitFor(() => expect(screen.getByText('winter@iscomming.com')).toBeInTheDocument)
})
......@@ -35,6 +35,9 @@ function SideMenu (props) {
<Nav.Item>
<Nav.Link as={Link} to="/orgs" eventKey="orgs">Orgs</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link as={Link} to="/apps/emails" eventKey="emails">Emails</Nav.Link>
</Nav.Item>
<hr/>
<Nav.Item>
<Nav.Link as={Link} to="/apps/domain">Domain</Nav.Link>
......
......@@ -4,6 +4,7 @@ import { Row, Col } from 'react-bootstrap'
import Console from '../components/console'
import AppNginx from '../components/apps/appNginx'
import AppDomain from '../components/apps/appDomain'
import AppEmail from '../components/apps/appEmail'
import axios from 'axios'
import RegisterPage from './Register'
import TopNav from '../components/top_nav'
......@@ -97,6 +98,7 @@ const ConsolePage = () => {
<Route>
<Route index element={<Console/>}/>
<Route path='apps/domain' element={<AppDomain org={org}/> }/>
<Route path='apps/emails' element={<AppEmail org={org}/> }/>
<Route path='apps/nginx' element={<AppNginx org={org}/> }/>
<Route path='*' element={<Navigate to='/'/>}/>
<Route path="register" element={<RegisterPage/> }/> {/* needed for tests :| */}
......
......@@ -6,6 +6,8 @@ usersservice:
dn: "cn=admin,dc=example,dc=com"
password:
password: pass123
endpoints:
apigateway: http://test-controller-gateway:5000/graphql
webui:
enabled: false
# image:
......@@ -18,6 +20,7 @@ gateway:
usersservice: "http://test-controller-usersservice:5000/users/"
appsservice: "http://test-controller-appsservice:5000/"
domainsservice: "http://test-controller-domainsservice:5000/"
emailsservice: "http://test-controller-usersservice:5000/"
landingpage:
ingress:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment