Skip to content
Snippets Groups Projects
Commit c7ef25b0 authored by Louis's avatar Louis :fire:
Browse files

Post / Get metrics

parent 6909138f
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
......@@ -14,6 +14,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@google-cloud/storage": "^4.1.3",
"dotenv": "^8.1.0",
"fs-jetpack": "^2.2.2",
"handlebars": "^4.5.3",
......
......@@ -42,7 +42,7 @@ class HttpError extends Error {
constructor(params) {
const {
id = insecureHexString(32),
status = '500',
status = 500,
code = 'X-000',
title = 'An Error Occurred',
description = 'Something went wrong, but no reason was given',
......
const timestamps = require('./properties/timestamps')
const metricConversionMap = {
trash: Number,
numeric: Number,
}
module.exports = (sequelize, DataTypes) => {
const Model = sequelize.define('Metric', Object.assign(
{
fake_pk: {
type: DataTypes.VIRTUAL,
primaryKey: true,
defaultValue: null,
},
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: false,
},
value: {
type: DataTypes.TEXT,
},
type: {
type: DataTypes.TEXT,
},
location: {
type: DataTypes.GEOGRAPHY('POINT', 4326),
get() {
const location = this.getDataValue('location')
return {
longitude: location.coordinates[0],
latitude: location.coordinates[1],
}
},
set(location) {
if (Array.isArray(location)) {
this.setDataValue('location', { type: 'Point', coordinates: location })
} else if (location.latitude && location.longitude) {
this.setDataValue('location', { type: 'Point', coordinated: [location.longitude, location.latitude] })
} else {
this.setDataValue('location', location)
}
},
},
recorded_at: {
type: DataTypes.DATE,
},
meta: {
type: DataTypes.JSONB,
},
deleted_at: {
type: DataTypes.DATE,
},
},
), {
paranoid: true,
timestamps: false,
tableName: 'metrics',
})
Model.getPolyIdentifier = () => 'metric'
Model.getRelationIdentifier = () => 'metrics'
Model.prototype.getNativeValue = function convertToNativeType() {
if (metricConversionMap.hasOwnProperty(this.type)) {
return metricConversionMap[this.type](this.value)
}
return this.value
}
Model.getSupportedMetricTypes = function() {
return Object.keys(metricConversionMap)
}
Model.prototype.toJSON = function userToJSON() {
const author = this.author ? { author: this.author } : {}
return {
id: this.id,
value: this.getNativeValue(),
type: this.type,
...author,
location: this.location,
recorded_at: this.recorded_at,
meta: this.meta,
}
}
Model.prototype.handleIncludes = async function(includes, loaders = null) {
}
Model.associate = function defineModelAssociations(models) {
Model.belongsTo(models.User, { as: 'author', foreignKey: 'author_id' })
}
Model.relations = [
]
return Model
}
......@@ -2,7 +2,9 @@ const ContextualModule = require('core/injection/ContextualModule')
const crypto = require('core/utils/crypto')
const HttpError = require('core/errors/HttpError')
module.exports = class UserService extends ContextualModule {
const BEARER_PREFIX = 'Bearer '
module.exports = class AuthService extends ContextualModule {
static getServiceName() {
return 'authService'
}
......@@ -53,8 +55,15 @@ module.exports = class UserService extends ContextualModule {
this.clearSessionAuth()
return null
}
} else if (this.ctx.get('Authorization')) {
const token = this.ctx.get('Authorization').substr(BEARER_PREFIX.length)
const user = await this.ctx.services.userService.findByAccessToken(token)
if (user) {
this.authenticateAs(user)
return user
}
}
return null // TODO: Get user from context
return null
}
authenticateAs(user) {
......
const ContextualModule = require('core/injection/ContextualModule')
const { User } = require('database/models')
const { User, AccessToken } = require('database/models')
module.exports = class UserService extends ContextualModule {
static getServiceName() { return 'userService' }
......@@ -12,6 +12,14 @@ module.exports = class UserService extends ContextualModule {
return User.findOne({ where: { email } })
}
async findByAccessToken(token) {
const accessToken = await AccessToken.findOne({ where: { token }, include: [{ model: User }] })
if (accessToken) {
return accessToken.User
}
return null
}
async register(name, email, password) {
const user = await User.register({
name,
......
const HttpError = require('core/errors/HttpError')
const { Sequelize, Metric } = require('database/models')
const { Op } = Sequelize
const moment = require('moment')
exports.postMetric = async ctx => {
const user = await ctx.services.authService.getUser()
const allowedTypes = new Set(Metric.getSupportedMetricTypes())
const { value, type, location } = ctx.request.body
if (location == null || location.longitude == null || location.latitude == null) {
throw new HttpError({
status: 400,
code: 'MTR-001',
title: 'Invalid Metric',
description: 'A location must be provided for metrics',
})
}
if (allowedTypes.has(type)) {
const point = { type: 'Point', coordinates: [location.longitude, location.latitude] }
const metric = await Metric.create({ value, type, location: point, author_id: user ? user.id : null })
ctx.body = { metric: metric.toJSON() }
} else {
throw new HttpError({ status: 400, code: 'MTR-002', title: 'Invalid Metric', description: `${ type } is not a supported type`})
}
}
function splitString(str) {
return str.split(',')
.map(s => s.trim())
.filter(Boolean)
}
function pointFromString(str) {
const parts = splitString(str)
.map(Number)
return {
latitude: parts[0],
longitude: parts[1],
}
}
exports.getWithin = async ctx => {
const {
point_from,
point_to,
date_from,
date_to = moment.utc(),
types = '',
} = ctx.request.query
const fromPoint = pointFromString(point_from)
const toPoint = pointFromString(point_to)
const fromDate = moment.utc(date_from)
const toDate = moment.utc(date_to)
const metricTypes = splitString(types)
const minFromLong = Math.min(fromPoint.longitude, toPoint.longitude)
const maxFromLong = Math.max(fromPoint.longitude, toPoint.longitude)
const minFromLat = Math.min(fromPoint.latitude, toPoint.latitude)
const maxFromLat = Math.max(fromPoint.latitude, toPoint.latitude)
const pointBuffer = [
[minFromLong, minFromLat],
[minFromLong, maxFromLat],
[maxFromLong, maxFromLat],
[maxFromLong, minFromLat],
[minFromLong, minFromLat],
].map(pb => pb.map(Number).join(' ')).join(',')
const metrics = await Metric.findAll({
where: {
[Op.and]: [
Sequelize.literal(`ST_COVEREDBY("Metric"."location", ST_POLYGONFROMTEXT('POLYGON((${ pointBuffer }))'))`),
],
recorded_at: {
[Op.between]: [fromDate.toISOString(), toDate.toISOString()]
},
type: {
[Op.in]: metricTypes,
}
},
})
console.log(metrics)
ctx.body = { metrics }
}
\ No newline at end of file
......@@ -2,10 +2,8 @@ const crypto = require('core/utils/crypto')
exports.login = async ctx => {
const { email, password } = ctx.request.body
console.log(email, password)
const user = await ctx.services.authService.attemptLogin(email, password)
ctx.services.authService.saveToSession()
return exports.handleLoginRedirect(ctx)
}
......@@ -13,7 +11,6 @@ exports.handleLoginRedirect = async ctx => {
if (ctx.request.query.login_state) {
const { login_state } = ctx.request.query
const values = JSON.parse(await crypto.decrypt(login_state))
console.log("handling lgoing redurect", values)
if (values.redirect === 'authorize') {
return ctx.redirect(`/auth/authorize?auth_state=${ values.query.auth_state }`)
} else {
......
......@@ -21,7 +21,8 @@ web.post('/auth/token', AuthServer.token)
const api = new Router({ prefix: '/api' })
api.use(require('./middleware/api/errors'))
api.get('/', controller('api/site', 'hello'))
api.post('/metrics', controller('api/content', 'postMetric'))
api.get('/metrics', controller('api/content', 'getWithin'))
api.post('/register', async ctx => {
const { name, email, password } = ctx.request.body
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment