mirror of
https://github.com/oceanprotocol/commons.git
synced 2023-03-15 18:03:00 +01:00
Merge pull request #172 from oceanprotocol/feature/reporting
Reporting data sets
This commit is contained in:
commit
b6770e68de
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,6 +12,7 @@ dist
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
|
|
17
README.md
17
README.md
|
@ -26,6 +26,8 @@ If you're a developer and want to contribute to, or want to utilize this marketp
|
||||||
- [🏖 Remote Ocean: Pacific](#-Remote-Ocean-Pacific)
|
- [🏖 Remote Ocean: Pacific](#-Remote-Ocean-Pacific)
|
||||||
- [🐳 Use with Barge](#-Use-with-Barge)
|
- [🐳 Use with Barge](#-Use-with-Barge)
|
||||||
- [⛵️ Environment Variables](#️-Environment-Variables)
|
- [⛵️ Environment Variables](#️-Environment-Variables)
|
||||||
|
- [Client](#Client)
|
||||||
|
- [Server](#Server)
|
||||||
- [👩🔬 Testing](#-Testing)
|
- [👩🔬 Testing](#-Testing)
|
||||||
- [Unit Tests](#Unit-Tests)
|
- [Unit Tests](#Unit-Tests)
|
||||||
- [End-to-End Integration Tests](#End-to-End-Integration-Tests)
|
- [End-to-End Integration Tests](#End-to-End-Integration-Tests)
|
||||||
|
@ -41,7 +43,7 @@ If you're a developer and want to contribute to, or want to utilize this marketp
|
||||||
This repo contains a client and a server, both written in TypeScript:
|
This repo contains a client and a server, both written in TypeScript:
|
||||||
|
|
||||||
- **client**: React app setup with [squid-js](https://github.com/oceanprotocol/squid-js), bootstrapped with [Create React App](https://github.com/facebook/create-react-app)
|
- **client**: React app setup with [squid-js](https://github.com/oceanprotocol/squid-js), bootstrapped with [Create React App](https://github.com/facebook/create-react-app)
|
||||||
- **server**: Node.js app, utilizing [Express](https://expressjs.com). The server provides various microservices, like remote file checking.
|
- **server**: Node.js app, utilizing [Express](https://expressjs.com). The server provides various microservices, like remote file checking. The endpoints are documented in [server Readme](server/).
|
||||||
|
|
||||||
To spin up both, the client and the server in a watch mode for local development, execute:
|
To spin up both, the client and the server in a watch mode for local development, execute:
|
||||||
|
|
||||||
|
@ -79,6 +81,8 @@ Modify `./client/src/config.ts` or set environment variables to use those local
|
||||||
|
|
||||||
### ⛵️ Environment Variables
|
### ⛵️ Environment Variables
|
||||||
|
|
||||||
|
#### Client
|
||||||
|
|
||||||
The `./client/src/config.ts` file is setup to prioritize environment variables for setting each Ocean component endpoint.
|
The `./client/src/config.ts` file is setup to prioritize environment variables for setting each Ocean component endpoint.
|
||||||
|
|
||||||
By setting environment variables, you can easily switch between Ocean networks the commons client connects to, without directly modifying `./client/src/config.ts`. This is helpful e.g. for local development so you don't accidentially commit changes to the config file.
|
By setting environment variables, you can easily switch between Ocean networks the commons client connects to, without directly modifying `./client/src/config.ts`. This is helpful e.g. for local development so you don't accidentially commit changes to the config file.
|
||||||
|
@ -92,6 +96,17 @@ cp client/.env.local.example client/.env.local
|
||||||
vi client/.env.local
|
vi client/.env.local
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Server
|
||||||
|
|
||||||
|
The server uses its own environment variables too:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp server/.env.example server/.env
|
||||||
|
|
||||||
|
# edit variables
|
||||||
|
vi server/.env
|
||||||
|
```
|
||||||
|
|
||||||
## 👩🔬 Testing
|
## 👩🔬 Testing
|
||||||
|
|
||||||
Test suite is setup with [Jest](https://jestjs.io) and [react-testing-library](https://github.com/kentcdodds/react-testing-library) for unit testing, and [Cypress](https://www.cypress.io) for integration testing.
|
Test suite is setup with [Jest](https://jestjs.io) and [react-testing-library](https://github.com/kentcdodds/react-testing-library) for unit testing, and [Cypress](https://www.cypress.io) for integration testing.
|
||||||
|
|
|
@ -52,3 +52,5 @@ REACT_APP_BRIZO_ADDRESS="0x008c25ed3594e094db4592f4115d5fa74c4f41ea"
|
||||||
# REACT_APP_SECRET_STORE_URI="http://localhost:12001"
|
# REACT_APP_SECRET_STORE_URI="http://localhost:12001"
|
||||||
# REACT_APP_FAUCET_URI="http://localhost:3001"
|
# REACT_APP_FAUCET_URI="http://localhost:3001"
|
||||||
# REACT_APP_BRIZO_ADDRESS="0x00bd138abd70e2f00903268f3db08f2d25677c9e"
|
# REACT_APP_BRIZO_ADDRESS="0x00bd138abd70e2f00903268f3db08f2d25677c9e"
|
||||||
|
|
||||||
|
REACT_APP_REPORT_EMAIL="test@example.com"
|
||||||
|
|
25
client/package-lock.json
generated
25
client/package-lock.json
generated
|
@ -1740,6 +1740,15 @@
|
||||||
"@types/react": "*"
|
"@types/react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-modal": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.8.2.tgz",
|
||||||
|
"integrity": "sha512-/Drs+XfHg9M60fy2Q63UUlhECXSNknDu3tnwFnbOhcdDjq03VD3hLCfv3X+BBzRqgu4TOu+TwEwBhgI8qdVAAQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/react-paginate": {
|
"@types/react-paginate": {
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-paginate/-/react-paginate-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-paginate/-/react-paginate-6.2.1.tgz",
|
||||||
|
@ -13136,6 +13145,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||||
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
||||||
},
|
},
|
||||||
|
"react-lifecycles-compat": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||||
|
},
|
||||||
"react-markdown": {
|
"react-markdown": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-4.1.0.tgz",
|
||||||
|
@ -13150,6 +13164,17 @@
|
||||||
"xtend": "^4.0.1"
|
"xtend": "^4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-modal": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.8.2.tgz",
|
||||||
|
"integrity": "sha512-wxNk94wy/DMh2LyJa8K+LyOQDhQfhKuBrZ4SxS091p75cpW+STfY+9GpAuvl6P6Yt2r/+wxYH8Z3G5Ww/L8Tiw==",
|
||||||
|
"requires": {
|
||||||
|
"exenv": "^1.2.0",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react-lifecycles-compat": "^3.0.0",
|
||||||
|
"warning": "^4.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-moment": {
|
"react-moment": {
|
||||||
"version": "0.9.2",
|
"version": "0.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.9.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-moment/-/react-moment-0.9.2.tgz",
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
"react-ga": "^2.6.0",
|
"react-ga": "^2.6.0",
|
||||||
"react-helmet": "^5.2.1",
|
"react-helmet": "^5.2.1",
|
||||||
"react-markdown": "^4.1.0",
|
"react-markdown": "^4.1.0",
|
||||||
|
"react-modal": "^3.8.2",
|
||||||
"react-moment": "^0.9.2",
|
"react-moment": "^0.9.2",
|
||||||
"react-paginate": "^6.3.0",
|
"react-paginate": "^6.3.0",
|
||||||
"react-popper": "^1.3.3",
|
"react-popper": "^1.3.3",
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
"@types/react-dom": "^16.8.4",
|
"@types/react-dom": "^16.8.4",
|
||||||
"@types/react-dotdotdot": "^1.2.0",
|
"@types/react-dotdotdot": "^1.2.0",
|
||||||
"@types/react-helmet": "^5.0.8",
|
"@types/react-helmet": "^5.0.8",
|
||||||
|
"@types/react-modal": "^3.8.2",
|
||||||
"@types/react-paginate": "^6.2.1",
|
"@types/react-paginate": "^6.2.1",
|
||||||
"@types/react-router-dom": "^4.3.4",
|
"@types/react-router-dom": "^4.3.4",
|
||||||
"@types/react-transition-group": "^2.9.2",
|
"@types/react-transition-group": "^2.9.2",
|
||||||
|
|
|
@ -12,6 +12,7 @@ interface ButtonProps {
|
||||||
onClick?: any
|
onClick?: any
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
to?: string
|
to?: string
|
||||||
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Button extends PureComponent<ButtonProps, any> {
|
export default class Button extends PureComponent<ButtonProps, any> {
|
||||||
|
|
88
client/src/components/atoms/Modal.module.scss
Normal file
88
client/src/components/atoms/Modal.module.scss
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
@import '../../styles/variables';
|
||||||
|
|
||||||
|
.modalOverlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba($brand-black, .7);
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
animation: fadeIn .2s ease-out backwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
padding: $spacer;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
background: $body-background;
|
||||||
|
margin: $spacer auto;
|
||||||
|
max-width: $break-point--small;
|
||||||
|
position: relative;
|
||||||
|
animation: moveUp .2s ease-out backwards;
|
||||||
|
|
||||||
|
@media (min-width: $break-point--small) {
|
||||||
|
padding: $spacer * 2 $spacer * 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-bottom: $spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: $font-size-h3;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
@media (min-width: $break-point--small) {
|
||||||
|
font-size: $font-size-h2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
margin: 0;
|
||||||
|
margin-top: $spacer / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: 0;
|
||||||
|
top: $spacer / 4;
|
||||||
|
right: $spacer / 2;
|
||||||
|
font-size: $font-size-h2;
|
||||||
|
color: $brand-grey;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes moveUp {
|
||||||
|
from {
|
||||||
|
transform: translate3d(0, 1rem, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
17
client/src/components/atoms/Modal.test.tsx
Normal file
17
client/src/components/atoms/Modal.test.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import React from 'react'
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import Modal from './Modal'
|
||||||
|
import ReactModal from 'react-modal'
|
||||||
|
|
||||||
|
describe('Modal', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
ReactModal.setAppElement(document.createElement('div'))
|
||||||
|
|
||||||
|
render(
|
||||||
|
<Modal title="Hello" isOpen toggleModal={() => null}>
|
||||||
|
Hello
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
expect(document.querySelector('.ReactModalPortal')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
51
client/src/components/atoms/Modal.tsx
Normal file
51
client/src/components/atoms/Modal.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import React from 'react'
|
||||||
|
import ReactModal from 'react-modal'
|
||||||
|
import styles from './Modal.module.scss'
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV !== 'test') ReactModal.setAppElement('#root')
|
||||||
|
|
||||||
|
const Modal = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
isOpen,
|
||||||
|
toggleModal,
|
||||||
|
children,
|
||||||
|
onAfterOpen,
|
||||||
|
onRequestClose,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
isOpen: boolean
|
||||||
|
toggleModal: () => void
|
||||||
|
children: any
|
||||||
|
onAfterOpen?: () => void
|
||||||
|
onRequestClose?: () => void
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<ReactModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onAfterOpen={onAfterOpen}
|
||||||
|
onRequestClose={onRequestClose}
|
||||||
|
contentLabel={title}
|
||||||
|
className={styles.modal}
|
||||||
|
overlayClassName={styles.modalOverlay}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<button className={styles.close} onClick={toggleModal}>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<header className={styles.header}>
|
||||||
|
<h2 className={styles.title}>{title}</h2>
|
||||||
|
{description && (
|
||||||
|
<p className={styles.description}>{description}</p>
|
||||||
|
)}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</ReactModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Modal
|
|
@ -5,6 +5,7 @@ import Markdown from '../../atoms/Markdown'
|
||||||
import CategoryLink from '../../atoms/CategoryLink'
|
import CategoryLink from '../../atoms/CategoryLink'
|
||||||
import styles from './AssetDetails.module.scss'
|
import styles from './AssetDetails.module.scss'
|
||||||
import AssetFilesDetails from './AssetFilesDetails'
|
import AssetFilesDetails from './AssetFilesDetails'
|
||||||
|
import Report from './Report'
|
||||||
|
|
||||||
interface AssetDetailsProps {
|
interface AssetDetailsProps {
|
||||||
metadata: MetaData
|
metadata: MetaData
|
||||||
|
@ -58,6 +59,8 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Report did={ddo.id} title={metadata.base.name} />
|
||||||
|
|
||||||
<div className={styles.metaFixed}>
|
<div className={styles.metaFixed}>
|
||||||
<h2
|
<h2
|
||||||
className={styles.metaFixedTitle}
|
className={styles.metaFixedTitle}
|
||||||
|
@ -97,10 +100,6 @@ export default class AssetDetails extends PureComponent<AssetDetailsProps> {
|
||||||
files={base.files ? base.files : []}
|
files={base.files ? base.files : []}
|
||||||
ddo={ddo}
|
ddo={ddo}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <pre>
|
|
||||||
<code>{JSON.stringify(metadata, null, 2)}</code>
|
|
||||||
</pre> */}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@ export default class AssetFile extends PureComponent<
|
||||||
// weird 0 hack so TypeScript is happy
|
// weird 0 hack so TypeScript is happy
|
||||||
onClick={() => this.purchaseAsset(ddo, index || 0)}
|
onClick={() => this.purchaseAsset(ddo, index || 0)}
|
||||||
disabled={!isLogged || !isOceanNetwork}
|
disabled={!isLogged || !isOceanNetwork}
|
||||||
|
name="Download"
|
||||||
>
|
>
|
||||||
Get file
|
Get file
|
||||||
</Button>
|
</Button>
|
||||||
|
|
49
client/src/components/templates/Asset/Report.module.scss
Normal file
49
client/src/components/templates/Asset/Report.module.scss
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
@import '../../../styles/variables';
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: $spacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openLink {
|
||||||
|
margin: 0;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
background: $brand-white;
|
||||||
|
padding: $spacer;
|
||||||
|
border: 1px solid $brand-grey-lighter;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: $font-size-base;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: $spacer / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
border-bottom: 1px solid $brand-grey-lighter;
|
||||||
|
padding-bottom: $spacer / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0;
|
||||||
|
color: $brand-grey-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background: $red;
|
||||||
|
padding: $spacer / 2;
|
||||||
|
text-align: center;
|
||||||
|
color: $brand-white;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
composes: error;
|
||||||
|
background: $green;
|
||||||
|
}
|
144
client/src/components/templates/Asset/Report.tsx
Normal file
144
client/src/components/templates/Asset/Report.tsx
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
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'
|
||||||
|
import Spinner from '../../atoms/Spinner'
|
||||||
|
|
||||||
|
export default class Report extends PureComponent<
|
||||||
|
{ did: string; title: string },
|
||||||
|
{
|
||||||
|
isModalOpen: boolean
|
||||||
|
comment: string
|
||||||
|
message: string
|
||||||
|
isSending: boolean
|
||||||
|
hasError?: boolean
|
||||||
|
hasSuccess?: boolean
|
||||||
|
}
|
||||||
|
> {
|
||||||
|
public state = {
|
||||||
|
isModalOpen: false,
|
||||||
|
comment: '',
|
||||||
|
message: 'Sending...',
|
||||||
|
isSending: false,
|
||||||
|
hasError: false,
|
||||||
|
hasSuccess: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
this.setState({ isSending: true })
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
to: process.env.REACT_APP_REPORT_EMAIL,
|
||||||
|
from: 'info@oceanprotocol.com',
|
||||||
|
subject: `[Report] ${this.props.title}`,
|
||||||
|
html: `<p>The following data set was reported:</p><p><strong>${this.props.title}</strong><br /><a style="color:#ff4092;text-decoration:none" href="https://commons.oceanprotocol.com/asset/${this.props.did}"><code>${this.props.did}</code></a></p><blockquote><em>${this.state.comment}</em></blockquote>`
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios({
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
url: `${serviceUri}/api/v1/report`,
|
||||||
|
data: { msg },
|
||||||
|
cancelToken: this.signal.token
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isSending: false,
|
||||||
|
hasSuccess: true,
|
||||||
|
message: 'Thanks for the report! We will take a look soon.'
|
||||||
|
})
|
||||||
|
return response.data.result
|
||||||
|
} catch (error) {
|
||||||
|
!axios.isCancel(error) &&
|
||||||
|
this.setState({
|
||||||
|
message: error.message,
|
||||||
|
isSending: false,
|
||||||
|
hasError: true
|
||||||
|
}) &&
|
||||||
|
Logger.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return (
|
||||||
|
<div className={styles.actions}>
|
||||||
|
<Button
|
||||||
|
link
|
||||||
|
className={styles.openLink}
|
||||||
|
onClick={this.toggleModal}
|
||||||
|
>
|
||||||
|
Report Data Set
|
||||||
|
</Button>
|
||||||
|
<Modal
|
||||||
|
title="Report Data Set"
|
||||||
|
description="Found some faulty metadata, wrongly attributed data, or anything else wrong with this data set? Tell us about it and we will take a look."
|
||||||
|
isOpen={this.state.isModalOpen}
|
||||||
|
toggleModal={this.toggleModal}
|
||||||
|
>
|
||||||
|
<div className={styles.info}>
|
||||||
|
<h3>{this.props.title}</h3>
|
||||||
|
<p>
|
||||||
|
<code>{this.props.did}</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{this.state.isSending ? (
|
||||||
|
<Spinner message={this.state.message} />
|
||||||
|
) : this.state.hasError ? (
|
||||||
|
<div className={styles.error}>
|
||||||
|
{this.state.message}
|
||||||
|
</div>
|
||||||
|
) : this.state.hasSuccess ? (
|
||||||
|
<div className={styles.success}>
|
||||||
|
{this.state.message}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Form minimal>
|
||||||
|
<Input
|
||||||
|
type="textarea"
|
||||||
|
name="comment"
|
||||||
|
label="Comment"
|
||||||
|
help="Briefly describe what is wrong with this asset. If you want to get contacted by us, add your email at the end."
|
||||||
|
required
|
||||||
|
value={this.state.comment}
|
||||||
|
onChange={this.inputChange}
|
||||||
|
rows={1}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
primary
|
||||||
|
onClick={(e: Event) => this.sendEmail(e)}
|
||||||
|
disabled={this.state.comment === ''}
|
||||||
|
>
|
||||||
|
Report Data Set
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -274,6 +274,9 @@ samp {
|
||||||
font-size: $font-size-small;
|
font-size: $font-size-small;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
h1 &,
|
h1 &,
|
||||||
h2 &,
|
h2 &,
|
||||||
|
|
|
@ -3,19 +3,27 @@ context('Consume', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.visit(`/asset/${Cypress.env('CONSUME_ASSET')}`)
|
cy.visit(`/asset/${Cypress.env('CONSUME_ASSET')}`)
|
||||||
|
|
||||||
// Wait for end of loading
|
// Alias button selector & wait for end of loading
|
||||||
cy.get('button', { timeout: 60000 }).should('have.length', 1)
|
cy.get('button[name="Download"]', { timeout: 60000 })
|
||||||
|
.first()
|
||||||
|
.should('have.length', 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get('button[name="Download"]')
|
||||||
|
.first()
|
||||||
|
.as('button')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Download button is clickable when user is connected.', () => {
|
it('Download button is clickable when user is connected.', () => {
|
||||||
cy.get('button').should('not.be.disabled')
|
cy.get('@button').should('not.be.disabled')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Consume asset and check if there is no error', () => {
|
it('Consume asset and check if there is no error', () => {
|
||||||
// Click consume button
|
// Click consume button
|
||||||
cy.get('button').click()
|
cy.get('@button').click()
|
||||||
// Wait consume process to end
|
// Wait consume process to end
|
||||||
cy.get('button', { timeout: 150000 }).should('contain', 'Get file')
|
cy.get('@button', { timeout: 150000 }).should('contain', 'Get file')
|
||||||
// check if there is no error
|
// check if there is no error
|
||||||
cy.get('article>div').should(
|
cy.get('article>div').should(
|
||||||
'not.contain',
|
'not.contain',
|
||||||
|
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -9943,9 +9943,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.4.5",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
|
||||||
"integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
|
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"stylelint-config-bigchaindb": "^1.2.2",
|
"stylelint-config-bigchaindb": "^1.2.2",
|
||||||
"stylelint-config-css-modules": "^1.4.0",
|
"stylelint-config-css-modules": "^1.4.0",
|
||||||
"stylelint-config-standard": "^18.3.0",
|
"stylelint-config-standard": "^18.3.0",
|
||||||
"typescript": "3.4.5"
|
"typescript": "3.5.2"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
1
server/.env.example
Normal file
1
server/.env.example
Normal file
|
@ -0,0 +1 @@
|
||||||
|
SENDGRID_API_KEY='xxx'
|
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 variables.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
```
|
96
server/package-lock.json
generated
96
server/package-lock.json
generated
|
@ -351,6 +351,34 @@
|
||||||
"@types/yargs": "^12.0.9"
|
"@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": {
|
"@types/babel__core": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz",
|
||||||
|
@ -405,8 +433,7 @@
|
||||||
"@types/caseless": {
|
"@types/caseless": {
|
||||||
"version": "0.12.2",
|
"version": "0.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
|
||||||
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
|
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@types/compression": {
|
"@types/compression": {
|
||||||
"version": "0.0.36",
|
"version": "0.0.36",
|
||||||
|
@ -463,7 +490,6 @@
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
|
||||||
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
|
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
|
@ -524,10 +550,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "11.13.15",
|
"version": "12.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.12.tgz",
|
||||||
"integrity": "sha512-x6ypl5Uzly+j23hbxmMzf12Eb4lOhIEqQz0HuczpTUa1KIx1GpbN/o4E3aAED20UoEsdK0wvyY8QcffuWSLDkw==",
|
"integrity": "sha512-Uy0PN4R5vgBUXFoJrKryf5aTk3kJ8Rv3PdlHjl6UaX+Cqp1QE0yPQ68MPXGrZOfG7gZVNDIJZYyot0B9ubXUrQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@types/range-parser": {
|
"@types/range-parser": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
|
@ -539,7 +564,6 @@
|
||||||
"version": "2.48.1",
|
"version": "2.48.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.1.tgz",
|
||||||
"integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==",
|
"integrity": "sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/caseless": "*",
|
"@types/caseless": "*",
|
||||||
"@types/form-data": "*",
|
"@types/form-data": "*",
|
||||||
|
@ -564,9 +588,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/superagent": {
|
"@types/superagent": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.2.tgz",
|
||||||
"integrity": "sha512-NetXrraTWPcdGG6IwYJhJ5esUGx8AYNiozbc1ENWEsF6BsD4JmNODJczI6Rm1xFPVp6HZESds9YCfqz4zIsM6A==",
|
"integrity": "sha512-GISrJnl+eZSzkVdsP2bXARXaroe/qKTwl/7v/d7bHP4OhlZKKIExcvQexwTDWHGtalHSLVuM78/Ri54laoOFfQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/cookiejar": "*",
|
"@types/cookiejar": "*",
|
||||||
|
@ -574,9 +598,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/supertest": {
|
"@types/supertest": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.8.tgz",
|
||||||
"integrity": "sha512-GibTh4OTkal71btYe2fpZP/rVHIPnnUsYphEaoywVHo+mo2a/LhlOFkIm5wdN0H0DA0Hx8x+tKgCYMD9elHu5w==",
|
"integrity": "sha512-wcax7/ip4XSSJRLbNzEIUVy2xjcBIZZAuSd2vtltQfRK7kxhx5WMHbLHkYdxN3wuQCrwpYrg86/9byDjPXoGMA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/superagent": "*"
|
"@types/superagent": "*"
|
||||||
|
@ -585,8 +609,7 @@
|
||||||
"@types/tough-cookie": {
|
"@types/tough-cookie": {
|
||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.5.tgz",
|
||||||
"integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==",
|
"integrity": "sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@types/yargs": {
|
"@types/yargs": {
|
||||||
"version": "12.0.12",
|
"version": "12.0.12",
|
||||||
|
@ -681,7 +704,6 @@
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"color-convert": "^1.9.0"
|
"color-convert": "^1.9.0"
|
||||||
}
|
}
|
||||||
|
@ -1117,7 +1139,6 @@
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-styles": "^3.2.1",
|
"ansi-styles": "^3.2.1",
|
||||||
"escape-string-regexp": "^1.0.5",
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
@ -1241,7 +1262,6 @@
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"color-name": "1.1.3"
|
"color-name": "1.1.3"
|
||||||
}
|
}
|
||||||
|
@ -1249,8 +1269,7 @@
|
||||||
"color-name": {
|
"color-name": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"combined-stream": {
|
"combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
|
@ -1504,6 +1523,11 @@
|
||||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
||||||
"dev": true
|
"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": {
|
"define-properties": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||||
|
@ -1605,6 +1629,11 @@
|
||||||
"is-obj": "^1.0.0"
|
"is-obj": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg=="
|
||||||
|
},
|
||||||
"duplexer3": {
|
"duplexer3": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
|
||||||
|
@ -1681,8 +1710,7 @@
|
||||||
"escape-string-regexp": {
|
"escape-string-regexp": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"escodegen": {
|
"escodegen": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
|
@ -1853,9 +1881,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"express-validator": {
|
"express-validator": {
|
||||||
"version": "6.0.1",
|
"version": "6.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.1.1.tgz",
|
||||||
"integrity": "sha512-hrvN512QBs8zKm2vu33a0AxomZBiDl/0jAYxoq3lnGdXrbWBvObGYugt8jmdfZbzoFDlT2ZF8jhYYszyjtdOjw==",
|
"integrity": "sha512-AF6YOhdDiCU7tUOO/OHp2W++I3qpYX7EInMmEEcRGOjs+qoubwgc5s6Wo3OQgxwsWRGCxXlrF73SIDEmY4y3wg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"validator": "^11.0.0"
|
"validator": "^11.0.0"
|
||||||
|
@ -2800,8 +2828,7 @@
|
||||||
"has-flag": {
|
"has-flag": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"has-symbols": {
|
"has-symbols": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -5537,7 +5564,6 @@
|
||||||
"version": "5.5.0",
|
"version": "5.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
@ -5793,9 +5819,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.4.5",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
|
||||||
"integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==",
|
"integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
|
@ -6031,9 +6057,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validator": {
|
"validator": {
|
||||||
"version": "11.0.0",
|
"version": "11.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-11.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/validator/-/validator-11.1.0.tgz",
|
||||||
"integrity": "sha512-+wnGLYqaKV2++nUv60uGzUJyJQwYVOin6pn1tgEiFCeCQO60yeu3Og9/yPccbBX574kxIcEJicogkzx6s6eyag=="
|
"integrity": "sha512-qiQ5ktdO7CD6C/5/mYV4jku/7qnqzjrxb3C/Q5wR3vGGinHTgJZN/TdFT3ZX4vXhX2R1PXx42fB1cn5W+uJ4lg=="
|
||||||
},
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
|
|
|
@ -13,11 +13,13 @@
|
||||||
"coverage": "cat coverage/lcov.info | codacy-coverage --token 8801f827fe1144ffa85cd7da94f2bbf7"
|
"coverage": "cat coverage/lcov.info | codacy-coverage --token 8801f827fe1144ffa85cd7da94f2bbf7"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sendgrid/mail": "^6.4.0",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
|
"dotenv": "^8.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-validator": "^6.0.1",
|
"express-validator": "^6.1.1",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"request": "^2.88.0"
|
"request": "^2.88.0"
|
||||||
},
|
},
|
||||||
|
@ -28,15 +30,15 @@
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
"@types/jest": "^24.0.15",
|
"@types/jest": "^24.0.15",
|
||||||
"@types/morgan": "^1.7.35",
|
"@types/morgan": "^1.7.35",
|
||||||
"@types/node": "^11.13.15",
|
"@types/node": "^12.0.12",
|
||||||
"@types/request": "^2.48.1",
|
"@types/request": "^2.48.1",
|
||||||
"@types/supertest": "^2.0.7",
|
"@types/supertest": "^2.0.8",
|
||||||
"jest": "^24.8.0",
|
"jest": "^24.8.0",
|
||||||
"nodemon": "^1.19.1",
|
"nodemon": "^1.19.1",
|
||||||
"supertest": "^4.0.2",
|
"supertest": "^4.0.2",
|
||||||
"ts-jest": "^24.0.2",
|
"ts-jest": "^24.0.2",
|
||||||
"ts-node": "^8.3.0",
|
"ts-node": "^8.3.0",
|
||||||
"typescript": "3.4.5"
|
"typescript": "3.5.2"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
36
server/src/routes/ReportRouter.ts
Normal file
36
server/src/routes/ReportRouter.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Router, Request, Response } from 'express'
|
||||||
|
import SendgridMail from '@sendgrid/mail'
|
||||||
|
import 'dotenv/config'
|
||||||
|
|
||||||
|
SendgridMail.setApiKey(process.env.SENDGRID_API_KEY)
|
||||||
|
|
||||||
|
export class ReportRouter {
|
||||||
|
public router: Router
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
this.router = Router()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendMessage(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) {
|
||||||
|
console.error(`${error.code} - ${error.message}`) // eslint-disable-line
|
||||||
|
res.send(`${error.code} - ${error.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
this.router.post('/', this.sendMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const reportRoutes = new ReportRouter()
|
||||||
|
reportRoutes.init()
|
||||||
|
|
||||||
|
export default reportRoutes.router
|
|
@ -7,6 +7,7 @@ import pkg from '../../package.json'
|
||||||
|
|
||||||
// routes
|
// routes
|
||||||
import UrlCheckRouter from './routes/UrlCheckRouter'
|
import UrlCheckRouter from './routes/UrlCheckRouter'
|
||||||
|
import ReportRouter from './routes/ReportRouter'
|
||||||
|
|
||||||
// config
|
// config
|
||||||
import config from './config/config'
|
import config from './config/config'
|
||||||
|
@ -62,6 +63,7 @@ app.get('/', (req, res) => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
app.use('/api/v1/urlcheck', UrlCheckRouter)
|
app.use('/api/v1/urlcheck', UrlCheckRouter)
|
||||||
|
app.use('/api/v1/report', ReportRouter)
|
||||||
|
|
||||||
/// catch 404
|
/// catch 404
|
||||||
app.use((req, res) => {
|
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', () => {
|
describe('Errors', () => {
|
||||||
it('responds with 404 on unknown path', async () => {
|
it('responds with 404 on unknown path', async () => {
|
||||||
const response = await request(server).post('/whatever')
|
const response = await request(server).post('/whatever')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user