mirror of
https://github.com/oceanprotocol/commons.git
synced 2023-03-15 18:03:00 +01:00
email sending via sendgrid
This commit is contained in:
parent
acb7ae4d35
commit
cee49978c4
@ -1,22 +1,66 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import React, { PureComponent, ChangeEvent } from 'react'
|
||||
import axios from 'axios'
|
||||
import { Logger } from '@oceanprotocol/squid'
|
||||
import Modal from '../../atoms/Modal'
|
||||
import styles from './Report.module.scss'
|
||||
import Button from '../../atoms/Button'
|
||||
import Input from '../../atoms/Form/Input'
|
||||
import Form from '../../atoms/Form/Form'
|
||||
import { serviceUri } from '../../../config'
|
||||
|
||||
export default class Report extends PureComponent<
|
||||
{ did: string; title: string },
|
||||
{ isModalOpen: boolean }
|
||||
{ isModalOpen: boolean; comment: string; error?: string }
|
||||
> {
|
||||
public state = {
|
||||
isModalOpen: false
|
||||
isModalOpen: false,
|
||||
comment: ''
|
||||
}
|
||||
|
||||
public toggleModal = () => {
|
||||
// for canceling axios requests
|
||||
public signal = axios.CancelToken.source()
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.signal.cancel()
|
||||
}
|
||||
|
||||
private inputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
comment: event.target.value
|
||||
})
|
||||
}
|
||||
|
||||
private toggleModal = () => {
|
||||
this.setState({ isModalOpen: !this.state.isModalOpen })
|
||||
}
|
||||
|
||||
private sendEmail = async (event: Event) => {
|
||||
event.preventDefault()
|
||||
|
||||
const msg = {
|
||||
to: 'test@example.com',
|
||||
from: 'test@example.com',
|
||||
subject: `[Report] ${this.props.title}`,
|
||||
html: `<p>The following data set was reported:</p><p><strong>${this.props.title}</strong><br /><a href="https://commons.oceanprotocol.com/asset/${this.props.did}"><code>${this.props.did}</code></a></p><blockquote><p>${this.state.comment}</p></blockquote>`
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios({
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
url: `${serviceUri}/api/v1/report`,
|
||||
data: { msg },
|
||||
cancelToken: this.signal.token
|
||||
})
|
||||
|
||||
return response.data.result
|
||||
} catch (error) {
|
||||
!axios.isCancel(error) &&
|
||||
this.setState({ error: error.message }) &&
|
||||
Logger.error(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className={styles.actions}>
|
||||
@ -46,8 +90,17 @@ export default class Report extends PureComponent<
|
||||
label="Comment"
|
||||
help="Briefly describe what is wrong with this asset."
|
||||
required
|
||||
value={this.state.comment}
|
||||
onChange={this.inputChange}
|
||||
rows={2}
|
||||
/>
|
||||
<Button primary>Report Data Set</Button>
|
||||
<Button
|
||||
primary
|
||||
onClick={(e: Event) => this.sendEmail(e)}
|
||||
disabled={this.state.comment === ''}
|
||||
>
|
||||
Report Data Set
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</Modal>
|
||||
|
118
server/README.md
Normal file
118
server/README.md
Normal file
@ -0,0 +1,118 @@
|
||||
[![banner](https://raw.githubusercontent.com/oceanprotocol/art/master/github/repo-banner%402x.png)](https://oceanprotocol.com)
|
||||
|
||||
<h1 align="center">Commons: Server</h1>
|
||||
|
||||
This folder contains server component written in TypeScript using [Express](https://expressjs.com). The server provides various microservices.
|
||||
|
||||
- [Get Started](#Get-Started)
|
||||
- [✨ API Documentation](#-API-Documentation)
|
||||
- [Url Checker](#Url-Checker)
|
||||
- [Report](#Report)
|
||||
- [🎁 Contributing](#-Contributing)
|
||||
- [🏛 License](#-License)
|
||||
|
||||
## Get Started
|
||||
|
||||
To spin up the server in a watch mode for local development, execute:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
|
||||
## ✨ API Documentation
|
||||
|
||||
### Url Checker
|
||||
|
||||
Url Checker returns size and additional information about requested file. This service is used as a solution to frontend CORS restrictions.
|
||||
|
||||
**Endpoint:** POST `/api/v1/urlcheck`
|
||||
|
||||
**Request Parameters**
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://oceanprotocol.com/tech-whitepaper.pdf"
|
||||
}
|
||||
```
|
||||
|
||||
**Response: Success**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"result": {
|
||||
"found": true,
|
||||
"contentLength": "2989228",
|
||||
"contentType": "application/pdf"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response: Error**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": null
|
||||
}
|
||||
```
|
||||
|
||||
### Report
|
||||
|
||||
Report endpoints sends an email via SendGrid. Requires `SENDGRID_API_KEY` set as environment variable.
|
||||
|
||||
**Endpoint:** POST `/api/v1/report`
|
||||
|
||||
**Request Parameters**
|
||||
|
||||
```json
|
||||
{
|
||||
"msg": {
|
||||
"to": "test@example.com",
|
||||
"from": "test@example.com",
|
||||
"subject": "My Subject",
|
||||
"text": "Text",
|
||||
"html": "<strong>HTML</strong>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response: Success**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
**Response: Error**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Error message"
|
||||
}
|
||||
```
|
||||
|
||||
## 🎁 Contributing
|
||||
|
||||
See the page titled "[Ways to Contribute](https://docs.oceanprotocol.com/concepts/contributing/)" in the Ocean Protocol documentation.
|
||||
|
||||
## 🏛 License
|
||||
|
||||
```text
|
||||
Copyright 2019 Ocean Protocol Foundation Ltd.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
57
server/package-lock.json
generated
57
server/package-lock.json
generated
@ -351,6 +351,34 @@
|
||||
"@types/yargs": "^12.0.9"
|
||||
}
|
||||
},
|
||||
"@sendgrid/client": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-6.4.0.tgz",
|
||||
"integrity": "sha512-GcO+hKXMQiwN0xMGfPITArlj4Nab1vZsrsRLmsJlcXGZV1V1zQC6XuAWJv6MGDd0hr/jKaXmCJ1XMYkxIRQHFw==",
|
||||
"requires": {
|
||||
"@sendgrid/helpers": "^6.4.0",
|
||||
"@types/request": "^2.0.3",
|
||||
"request": "^2.88.0"
|
||||
}
|
||||
},
|
||||
"@sendgrid/helpers": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-6.4.0.tgz",
|
||||
"integrity": "sha512-1dDDXauArHyxwTKFFfWvQpsijmwalyLgwoQJ3FRCssFq1RfqYDgFhRg0Xs3v/IXS2jkKWePSWiPORSR4Sysdpw==",
|
||||
"requires": {
|
||||
"chalk": "^2.0.1",
|
||||
"deepmerge": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"@sendgrid/mail": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-6.4.0.tgz",
|
||||
"integrity": "sha512-pVzbqbxhZ4FUN6iSIksRLtyXRPurrcee1i0noPDStDCLlHVwUR+TofeeKIFWGpIvbbk5UR6S6iV/U5ie8Kdblw==",
|
||||
"requires": {
|
||||
"@sendgrid/client": "^6.4.0",
|
||||
"@sendgrid/helpers": "^6.4.0"
|
||||
}
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz",
|
||||
@ -405,8 +433,7 @@
|
||||
"@types/caseless": {
|
||||
"version": "0.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
|
||||
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
|
||||
},
|
||||
"@types/compression": {
|
||||
"version": "0.0.36",
|
||||
@ -463,7 +490,6 @@
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
||||
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@ -526,8 +552,7 @@
|
||||
"@types/node": {
|
||||
"version": "11.13.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.15.tgz",
|
||||
"integrity": "sha512-x6ypl5Uzly+j23hbxmMzf12Eb4lOhIEqQz0HuczpTUa1KIx1GpbN/o4E3aAED20UoEsdK0wvyY8QcffuWSLDkw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-x6ypl5Uzly+j23hbxmMzf12Eb4lOhIEqQz0HuczpTUa1KIx1GpbN/o4E3aAED20UoEsdK0wvyY8QcffuWSLDkw=="
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
@ -539,7 +564,6 @@
|
||||
"version": "2.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz",
|
||||
"integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/caseless": "*",
|
||||
"@types/form-data": "*",
|
||||
@ -585,8 +609,7 @@
|
||||
"@types/tough-cookie": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz",
|
||||
"integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg=="
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "12.0.12",
|
||||
@ -681,7 +704,6 @@
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
@ -1117,7 +1139,6 @@
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
@ -1241,7 +1262,6 @@
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@ -1249,8 +1269,7 @@
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
@ -1504,6 +1523,11 @@
|
||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
||||
"dev": true
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
|
||||
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
@ -1681,8 +1705,7 @@
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.11.1",
|
||||
@ -2800,8 +2823,7 @@
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||
"dev": true
|
||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.0",
|
||||
@ -5537,7 +5559,6 @@
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
"coverage": "cat coverage/lcov.info | codacy-coverage --token 8801f827fe1144ffa85cd7da94f2bbf7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sendgrid/mail": "^6.4.0",
|
||||
"body-parser": "^1.18.3",
|
||||
"compression": "^1.7.4",
|
||||
"debug": "^4.1.1",
|
||||
|
34
server/src/routes/ReportRouter.ts
Normal file
34
server/src/routes/ReportRouter.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Router, Request, Response } from 'express'
|
||||
import SendgridMail from '@sendgrid/mail'
|
||||
|
||||
SendgridMail.setApiKey(process.env.SENDGRID_API_KEY)
|
||||
|
||||
export class ReportRouter {
|
||||
public router: Router
|
||||
|
||||
public constructor() {
|
||||
this.router = Router()
|
||||
}
|
||||
|
||||
public async sendEmail(req: Request, res: Response) {
|
||||
if (!req.body.msg) {
|
||||
return res.send({ status: 'error', message: 'missing message' })
|
||||
}
|
||||
|
||||
try {
|
||||
await SendgridMail.send(req.body.msg)
|
||||
return res.send({ status: 'success' })
|
||||
} catch (error) {
|
||||
res.send(`${error.code} - ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
public init() {
|
||||
this.router.post('/', this.sendEmail)
|
||||
}
|
||||
}
|
||||
|
||||
const reportRoutes = new ReportRouter()
|
||||
reportRoutes.init()
|
||||
|
||||
export default reportRoutes.router
|
@ -7,6 +7,7 @@ import pkg from '../../package.json'
|
||||
|
||||
// routes
|
||||
import UrlCheckRouter from './routes/UrlCheckRouter'
|
||||
import ReportRouter from './routes/ReportRouter'
|
||||
|
||||
// config
|
||||
import config from './config/config'
|
||||
@ -62,6 +63,7 @@ app.get('/', (req, res) => {
|
||||
)
|
||||
})
|
||||
app.use('/api/v1/urlcheck', UrlCheckRouter)
|
||||
app.use('/api/v1/report', ReportRouter)
|
||||
|
||||
/// catch 404
|
||||
app.use((req, res) => {
|
||||
|
@ -25,6 +25,14 @@ describe('POST /api/v1/urlcheck', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /api/v1/report', () => {
|
||||
it('responds with error message when message is missing', async () => {
|
||||
const response = await request(server).post('/api/v1/report')
|
||||
const text = await JSON.parse(response.text)
|
||||
expect(text.message).toBe('missing message')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Errors', () => {
|
||||
it('responds with 404 on unknown path', async () => {
|
||||
const response = await request(server).post('/whatever')
|
||||
|
Loading…
Reference in New Issue
Block a user