Skip to content

Commit f13e31a

Browse files
committed
start on data anslyzer
1 parent 2ff3dac commit f13e31a

29 files changed

+1669
-2944
lines changed

.firebaserc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"projects": {
3+
"default": "data-collector-ff33b"
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# This file was auto-generated by the Firebase CLI
2+
# https://github.com/firebase/firebase-tools
3+
4+
name: Deploy to Firebase Hosting on merge
5+
'on':
6+
push:
7+
branches:
8+
- main
9+
jobs:
10+
build_and_deploy:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v2
14+
- run: pnpm build
15+
- uses: FirebaseExtended/action-hosting-deploy@v0
16+
with:
17+
repoToken: '${{ secrets.GITHUB_TOKEN }}'
18+
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_DATA_COLLECTOR_FF33B }}'
19+
channelId: live
20+
projectId: data-collector-ff33b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# This file was auto-generated by the Firebase CLI
2+
# https://github.com/firebase/firebase-tools
3+
4+
name: Deploy to Firebase Hosting on PR
5+
'on': pull_request
6+
jobs:
7+
build_and_preview:
8+
if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}'
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- run: pnpm build
13+
- uses: FirebaseExtended/action-hosting-deploy@v0
14+
with:
15+
repoToken: '${{ secrets.GITHUB_TOKEN }}'
16+
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_DATA_COLLECTOR_FF33B }}'
17+
projectId: data-collector-ff33b

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ packages/*/dist
66
.idea
77
packages/*/coverage
88
.vscode/
9-
dist
9+
dist
10+
serviceAccount.json
11+
**.log

firebase.json

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"hosting": {
3+
"public": "dist",
4+
"ignore": [
5+
"firebase.json",
6+
"**/.*",
7+
"**/node_modules/**"
8+
]
9+
},
10+
"emulators": {
11+
"auth": {
12+
"port": 9099
13+
},
14+
"firestore": {
15+
"port": 8080
16+
},
17+
"ui": {
18+
"enabled": true
19+
}
20+
}
21+
}

packages/data-analyzer/package.json

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "data-analyzer",
3+
"version": "1.0.0",
4+
"description": "Responsible for serving data-collector and data-presenter",
5+
"main": "src/wrapper.ts",
6+
"scripts": {
7+
"dev": "esbuild src/** --define:process.env.NODE_ENV=\"production\" --bundle --sourcemap --outdir=./dist --watch",
8+
"test": "jest --watchAll --no-cache --detectOpenHandles",
9+
"prettier": "prettier --check src/",
10+
"prettier:fix": "prettier --write src/",
11+
"lint": "eslint . --ext .ts,.tsx",
12+
"lint:fix": "yarn lint --fix",
13+
"verify": "run-p prettier lint",
14+
"verify:fix": "yarn prettier:fix && yarn lint:fix",
15+
"build": "esbuild src/** --define:process.env.NODE_ENV=\"production\" --bundle --sourcemap --outdir=./dist"
16+
},
17+
"author": "Jesper Paulsen",
18+
"license": "MIT",
19+
"devDependencies": {
20+
"@types/chrome": "^0.0.154",
21+
"@types/cors": "^2.8.12",
22+
"@types/node": "^16.7.12",
23+
"supertest": "^6.1.6"
24+
},
25+
"publishConfig": {
26+
"access": "public",
27+
"main": "dist/index.js",
28+
"typings": "dist/src/index.d.ts"
29+
},
30+
"dependencies": {
31+
"cors": "^2.8.5",
32+
"express": "^4.17.1",
33+
"express-validator": "^6.12.1",
34+
"firebase-admin": "^9.11.1"
35+
}
36+
}

packages/data-analyzer/src/app.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { json } from 'body-parser'
2+
import cors from 'cors'
3+
import express from 'express'
4+
5+
import 'express-async-errors'
6+
7+
import { NotFoundError } from './errors/not-found-error'
8+
import { currentUser } from './middlewares/current-user'
9+
import { errorHandler } from './middlewares/error-handler'
10+
11+
const app = express()
12+
13+
app.use(json())
14+
app.use(cors())
15+
16+
app.use(currentUser)
17+
18+
app.all('*', async (req, res, next) => {
19+
throw new NotFoundError()
20+
})
21+
22+
app.use(errorHandler)
23+
24+
export { app }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { CustomError } from './custom-error'
2+
3+
export class BadRequestError extends CustomError {
4+
statusCode = 400
5+
message = ''
6+
7+
constructor(message: string) {
8+
super(message)
9+
10+
this.message = message
11+
Object.setPrototypeOf(this, BadRequestError.prototype)
12+
}
13+
14+
serializeErrors() {
15+
return [{ message: this.message }]
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export abstract class CustomError extends Error {
2+
abstract statusCode: number
3+
4+
constructor(message: string) {
5+
super(message)
6+
7+
Object.setPrototypeOf(this, CustomError.prototype)
8+
}
9+
10+
abstract serializeErrors(): { message: string; fields?: string[] }[]
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { CustomError } from './custom-error'
2+
3+
export class DatabaseConnectionError extends CustomError {
4+
statusCode = 500
5+
reason = 'Error connecting to database'
6+
7+
constructor(reason?: string) {
8+
super('Eror connecting to DB')
9+
this.reason = reason || this.reason
10+
Object.setPrototypeOf(this, DatabaseConnectionError.prototype)
11+
}
12+
13+
serializeErrors() {
14+
return [{ message: this.reason }]
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { CustomError } from './custom-error'
2+
3+
export class NotAuthorizedError extends CustomError {
4+
statusCode = 401
5+
6+
constructor() {
7+
super('Not authorized')
8+
9+
Object.setPrototypeOf(this, NotAuthorizedError.prototype)
10+
}
11+
12+
serializeErrors() {
13+
return [{ message: 'Not authorized' }]
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { CustomError } from './custom-error'
2+
3+
export class NotFoundError extends CustomError {
4+
statusCode = 404
5+
6+
constructor() {
7+
super('Route not found')
8+
9+
Object.setPrototypeOf(this, NotFoundError.prototype)
10+
}
11+
12+
serializeErrors() {
13+
return [{ message: 'Not found' }]
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ValidationError } from 'express-validator'
2+
3+
import { CustomError } from './custom-error'
4+
5+
export class RequestValidationError extends CustomError {
6+
statusCode = 400
7+
constructor(public errors: ValidationError[]) {
8+
super('Invalid request parameters')
9+
10+
Object.setPrototypeOf(this, RequestValidationError.prototype)
11+
}
12+
13+
serializeErrors() {
14+
return this.errors.map((error) => {
15+
return { message: error.msg, field: error.param }
16+
})
17+
}
18+
}

packages/data-analyzer/src/index.ts

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { NextFunction, Request, Response } from 'express'
2+
import admin from 'firebase-admin'
3+
4+
export const currentUser = async (
5+
req: Request,
6+
res: Response,
7+
next: NextFunction
8+
) => {
9+
let idToken = ''
10+
11+
if (
12+
req.headers.authorization &&
13+
req.headers.authorization.startsWith('Bearer ')
14+
) {
15+
idToken = req.headers.authorization.split('Bearer ')[1]
16+
} else if (req.cookies) {
17+
idToken = req.cookies.__session
18+
}
19+
try {
20+
const decodedIdToken = await admin.auth().verifyIdToken(idToken, true)
21+
req.currentUser = decodedIdToken
22+
} catch (e) {
23+
console.log(e)
24+
req.currentUser = undefined
25+
}
26+
next()
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { NextFunction, Request, Response } from 'express'
2+
3+
import { CustomError } from '../errors/custom-error'
4+
5+
export const errorHandler = (
6+
err: Error,
7+
req: Request,
8+
res: Response,
9+
next: NextFunction
10+
) => {
11+
if (err instanceof CustomError)
12+
return res.status(err.statusCode).send({ errors: err.serializeErrors() })
13+
14+
console.error(err)
15+
return res
16+
.status(400)
17+
.send({ errors: [{ message: 'Something went wrong!' }] })
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { NextFunction, Request, Response } from 'express'
2+
3+
import { NotAuthorizedError } from '../errors/not-authorized-error'
4+
5+
export const requireAdmin = (
6+
req: Request,
7+
res: Response,
8+
next: NextFunction
9+
) => {
10+
const user = req.currentUser
11+
if (!user) throw new NotAuthorizedError()
12+
const role = user.role
13+
if (!role || role !== 'admin') throw new NotAuthorizedError()
14+
next()
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { NextFunction, Request, Response } from 'express'
2+
3+
import { NotAuthorizedError } from '../errors/not-authorized-error'
4+
5+
export const requireAuth = (
6+
req: Request,
7+
res: Response,
8+
next: NextFunction
9+
) => {
10+
if (!req.currentUser) {
11+
throw new NotAuthorizedError()
12+
}
13+
14+
next()
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { NextFunction, Request, Response } from 'express'
2+
import { validationResult } from 'express-validator'
3+
4+
import { RequestValidationError } from '../errors/request-validation-error'
5+
6+
export const validateRequest = (
7+
req: Request,
8+
res: Response,
9+
next: NextFunction
10+
) => {
11+
const errors = validationResult(req)
12+
if (!errors.isEmpty()) {
13+
throw new RequestValidationError(errors.array())
14+
}
15+
16+
next()
17+
}

packages/data-analyzer/src/routes/__test__/network-call.test.ts

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import request from 'supertest'
2+
3+
import { app } from '../../app'
4+
5+
describe('route: /users method: POST', () => {
6+
it('creates a user sucessfully', async () => {})
7+
})

packages/data-analyzer/src/routes/network-call.ts

Whitespace-only changes.
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import express, { NextFunction, Request, Response } from 'express'
2+
import { checkSchema, param } from 'express-validator'
3+
4+
import { DatabaseConnectionError } from '../errors/database-connection-error'
5+
import { requireAdmin } from '../middlewares/require-admin'
6+
import { requireAuth } from '../middlewares/require-auth'
7+
import { validateRequest } from '../middlewares/validate-request'
8+
import UserSchema from '../schemas/UserSchema'
9+
import firebaseAdmin from '../services/firebase-admin'
10+
11+
const basePath = '/user'
12+
13+
const generateRoute = (path = '') => {
14+
return `${basePath}${path}`
15+
}
16+
17+
const router = express.Router()
18+
19+
router.post(
20+
generateRoute(),
21+
checkSchema(UserSchema()),
22+
validateRequest,
23+
async (req: Request, res: Response, next: NextFunction) => {
24+
try {
25+
// pass
26+
} catch (e) {
27+
next(new DatabaseConnectionError(e.message))
28+
}
29+
}
30+
)
31+
32+
router.put(
33+
generateRoute('/admin/:userId'),
34+
validateRequest,
35+
async (req: Request, res: Response, next: NextFunction) => {}
36+
)

0 commit comments

Comments
 (0)