diff --git a/src/helpers/asyncForEach.js b/src/helpers/asyncForEach.js new file mode 100644 index 000000000..1f05ea915 --- /dev/null +++ b/src/helpers/asyncForEach.js @@ -0,0 +1,14 @@ +/** + * Provide a way to iterate for each element in an array while waiting for async functions to finish + * + * @param array + * @param callback + * @returns {Promise} + */ +async function asyncForEach (array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array) + } +} + +export default asyncForEach diff --git a/src/middleware/helpers/walkRecursive.js b/src/helpers/walkRecursive.js similarity index 100% rename from src/middleware/helpers/walkRecursive.js rename to src/helpers/walkRecursive.js diff --git a/src/middleware/nodes/locations.js b/src/middleware/nodes/locations.js new file mode 100644 index 000000000..735b047dd --- /dev/null +++ b/src/middleware/nodes/locations.js @@ -0,0 +1,124 @@ + +import request from 'request' +import { UserInputError } from 'apollo-server' +import isEmpty from 'lodash/isEmpty' +import asyncForEach from '../../helpers/asyncForEach' + +const fetch = url => { + return new Promise((resolve, reject) => { + request(url, function (error, response, body) { + if (error) { + reject(error) + } else { + resolve(JSON.parse(body)) + } + }) + }) +} + +const locales = [ + 'en', + 'de', + 'fr', + 'nl', + 'it', + 'es', + 'pt', + 'pl' +] + +const createLocation = async (session, mapboxData) => { + const data = { + id: mapboxData.id, + nameEN: mapboxData.text_en, + nameDE: mapboxData.text_de, + nameFR: mapboxData.text_fr, + nameNL: mapboxData.text_nl, + nameIT: mapboxData.text_it, + nameES: mapboxData.text_es, + namePT: mapboxData.text_pt, + namePL: mapboxData.text_pl, + type: mapboxData.id.split('.')[0].toLowerCase(), + lat: (mapboxData.center && mapboxData.center.length) ? mapboxData.center[0] : null, + lng: (mapboxData.center && mapboxData.center.length) ? mapboxData.center[1] : null + } + + let query = 'MERGE (l:Location {id: $id}) ' + + 'SET l.name = $nameEN, ' + + 'l.nameEN = $nameEN, ' + + 'l.nameDE = $nameDE, ' + + 'l.nameFR = $nameFR, ' + + 'l.nameNL = $nameNL, ' + + 'l.nameIT = $nameIT, ' + + 'l.nameES = $nameES, ' + + 'l.namePT = $namePT, ' + + 'l.namePL = $namePL, ' + + 'l.type = $type' + + if (data.lat && data.lng) { + query += ', l.lat = $lat, l.lng = $lng' + } + query += ' RETURN l.id' + + await session.run(query, data) +} + +const createOrUpdateLocations = async (userId, locationName, driver) => { + if (isEmpty(locationName)) { + return + } + const mapboxToken = process.env.MAPBOX_TOKEN + const res = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(locationName)}.json?access_token=${mapboxToken}&types=region,place,country&language=${locales.join(',')}`) + + if (!res || !res.features || !res.features[0]) { + throw new UserInputError('locationName is invalid') + } + + let data + + res.features.forEach(item => { + if (item.matching_place_name === locationName) { + data = item + } + }) + if (!data) { + data = res.features[0] + } + + if (!data || !data.place_type || !data.place_type.length) { + throw new UserInputError('locationName is invalid') + } + + const session = driver.session() + await createLocation(session, data) + + let parent = data + + if (data.context) { + await asyncForEach(data.context, async ctx => { + await createLocation(session, ctx) + + await session.run( + 'MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId}) ' + + 'MERGE (child)<-[:IS_IN]-(parent) ' + + 'RETURN child.id, parent.id', { + parentId: parent.id, + childId: ctx.id + }) + + parent = ctx + }) + } + // delete all current locations from user + await session.run('MATCH (u:User {id: $userId})-[r:IS_IN]->(l:Location) DETACH DELETE r', { + userId: userId + }) + // connect user with location + await session.run('MATCH (u:User {id: $userId}), (l:Location {id: $locationId}) MERGE (u)-[:IS_IN]->(l) RETURN l.id, u.id', { + userId: userId, + locationId: data.id + }) + session.close() +} + +export default createOrUpdateLocations diff --git a/src/middleware/passwordMiddleware.js b/src/middleware/passwordMiddleware.js index 4f551fe5d..16480b126 100644 --- a/src/middleware/passwordMiddleware.js +++ b/src/middleware/passwordMiddleware.js @@ -1,5 +1,5 @@ import bcrypt from 'bcryptjs' -import walkRecursive from './helpers/walkRecursive' +import walkRecursive from '../helpers/walkRecursive' export default { Mutation: { diff --git a/src/middleware/userMiddleware.js b/src/middleware/userMiddleware.js index adb6eb46f..55b181bc9 100644 --- a/src/middleware/userMiddleware.js +++ b/src/middleware/userMiddleware.js @@ -1,136 +1,4 @@ -import request from 'request' -import { UserInputError } from 'apollo-server' -import isEmpty from 'lodash/isEmpty' - -const asyncForEach = async (array, callback) => { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array) - } -} - -const fetch = url => { - return new Promise((resolve, reject) => { - request(url, function (error, response, body) { - if (error) { - reject(error) - } else { - resolve(JSON.parse(body)) - } - }) - }) -} - -const createOrUpdateLocations = async (userId, locationName, driver) => { - if (isEmpty(locationName)) { - return - } - const mapboxToken = process.env.MAPBOX_TOKEN - const res = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent(locationName)}.json?access_token=${mapboxToken}&types=region,place,country&language=en,de,fr,nl,it,es,pt,pl`) - - if (!res || !res.features || !res.features[0]) { - throw new UserInputError('locationName is invalid') - } - - let data - - res.features.forEach(item => { - if (item.matching_place_name === locationName) { - data = item - } - }) - if (!data) { - data = res.features[0] - } - - if (!data) { - throw new UserInputError('locationName is invalid') - } - - const session = driver.session() - await session.run( - 'MERGE (l:Location {id: $id}) ' + - 'SET l.name = $nameEN, ' + - 'l.nameEN = $nameEN, ' + - 'l.nameDE = $nameDE, ' + - 'l.nameFR = $nameFR, ' + - 'l.nameNL = $nameNL, ' + - 'l.nameIT = $nameIT, ' + - 'l.nameES = $nameES, ' + - 'l.namePT = $namePT, ' + - 'l.namePL = $namePL, ' + - 'l.type = $type, ' + - 'l.lat = $lat, ' + - 'l.lng = $lng ' + - 'RETURN l.id', { - id: data.id, - nameEN: data.text_en, - nameDE: data.text_de, - nameFR: data.text_fr, - nameNL: data.text_nl, - nameIT: data.text_it, - nameES: data.text_es, - namePT: data.text_pt, - namePL: data.text_pl, - type: data.place_type[0].toLowerCase(), - lat: data.center[0], - lng: data.center[1] - } - ) - - let parent = data - - if (data.context) { - await asyncForEach(data.context, async ctx => { - const type = ctx.id.split('.')[0].toLowerCase() - await session.run( - 'MERGE (l:Location {id: $id}) ' + - 'SET l.name = $nameEN, ' + - 'l.nameEN = $nameEN, ' + - 'l.nameDE = $nameDE, ' + - 'l.nameFR = $nameFR, ' + - 'l.nameNL = $nameNL, ' + - 'l.nameIT = $nameIT, ' + - 'l.nameES = $nameES, ' + - 'l.namePT = $namePT, ' + - 'l.namePL = $namePL, ' + - 'l.type = $type, ' + - 'l.shortCode = $shortCode ' + - 'RETURN l.id', { - id: ctx.id, - nameEN: ctx.text_en, - nameDE: ctx.text_de, - nameFR: ctx.text_fr, - nameNL: ctx.text_nl, - nameIT: ctx.text_it, - nameES: ctx.text_es, - namePT: ctx.text_pt, - namePL: ctx.text_pl, - type: type, - shortCode: ctx.short_code - } - ) - await session.run( - 'MATCH (parent:Location {id: $parentId}), (child:Location {id: $childId}) ' + - 'MERGE (child)<-[:IS_IN]-(parent) ' + - 'RETURN child.id, parent.id', { - parentId: parent.id, - childId: ctx.id - }) - - parent = ctx - }) - } - // delete all current locations from user - await session.run('MATCH (u:User {id: $userId})-[r:IS_IN]->(l:Location) DETACH DELETE r', { - userId: userId - }) - // connect user with location - await session.run('MATCH (u:User {id: $userId}), (l:Location {id: $locationId}) MERGE (u)-[:IS_IN]->(l) RETURN l.id, u.id', { - userId: userId, - locationId: data.id - }) - session.close() -} +import createOrUpdateLocations from './nodes/locations' export default { Mutation: { diff --git a/src/middleware/xssMiddleware.js b/src/middleware/xssMiddleware.js index ac71f3421..59ce8800f 100644 --- a/src/middleware/xssMiddleware.js +++ b/src/middleware/xssMiddleware.js @@ -1,4 +1,4 @@ -import walkRecursive from './helpers/walkRecursive' +import walkRecursive from '../helpers/walkRecursive' // import { getByDot, setByDot, getItems, replaceItems } from 'feathers-hooks-common' import sanitizeHtml from 'sanitize-html' // import { isEmpty, intersection } from 'lodash' diff --git a/src/seed/data/index.js b/src/seed/data/index.js index e50fececc..33bc56f36 100644 --- a/src/seed/data/index.js +++ b/src/seed/data/index.js @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import helper from '../seed-helpers' +import asyncForEach from '../../helpers/asyncForEach' const seed = { Badge: require('./badges.js').default, @@ -22,7 +22,7 @@ let data = {} export default async function (client) { // iterate through seeds - await helper.asyncForEach(Object.keys(seed), async key => { + await asyncForEach(Object.keys(seed), async key => { const mutations = seed[key] try { const res = await client diff --git a/src/seed/seed-helpers.js b/src/seed/seed-helpers.js index 48b9ba01d..23bde40ae 100644 --- a/src/seed/seed-helpers.js +++ b/src/seed/seed-helpers.js @@ -46,7 +46,7 @@ export default { let randomIds = _.shuffle(ids) return items[randomIds.pop()] }, - randomItems: (items, key = '_id', min = 1, max = 1) => { + randomItems: (items, key = 'id', min = 1, max = 1) => { let randomIds = _.shuffle(_.keys(items)) let res = [] @@ -54,7 +54,7 @@ export default { for (let i = 0; i < count; i++) { let r = items[randomIds.pop()][key] - if (key === '_id') { + if (key === 'id') { r = r.toString() } res.push(r) @@ -117,22 +117,10 @@ export default { mapIdsByKey: (items, values, key) => { let res = [] values.forEach(value => { - res.push(_.find(items, [key, value])._id.toString()) + res.push(_.find(items, [key, value]).id.toString()) }) return res }, - /** - * Provide a way to iterate for each element in an array while waiting for async functions to finish - * - * @param array - * @param callback - * @returns {Promise} - */ - asyncForEach: async (array, callback) => { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array) - } - }, genInviteCode: () => { const chars = '23456789abcdefghkmnpqrstuvwxyzABCDEFGHJKLMNPRSTUVWXYZ' let code = ''