Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

database connection refactored #118

Merged
merged 5 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

### Changed

### Fixed

### Deprecated

- route '/api/staticLayers', use '/api/dataLayers' [111]()

### Removed

## [1.3.2] - 2022-02-21

### Added
Expand Down
4 changes: 2 additions & 2 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const appInsights = require('applicationinsights');
const config = require('./src/config/config');
const logger = require('./src/config/winston');
const { initializeDBConnection } = require('./src/db');
const { initializeDb } = require('./src/database');

if (config.appInsightsInstrumentationKey) {
appInsights.setup().start();
Expand All @@ -10,7 +10,7 @@ const app = require('./src/config/express');

const startApp = async () => {
try {
await initializeDBConnection();
await initializeDb();

app.listen(config.port, () => {
logger.info(`Oscar API running and listening on port ${config.port}`);
Expand Down
2 changes: 1 addition & 1 deletion api/src/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const envVarsSchema = Joi.object({
NODE_ENV: Joi.string().allow('development', 'production', 'test', 'provision').default('development'),
PORT: Joi.number().default(8080),
LOG_LABEL: Joi.string().default('oscar-api'),
MONGO_URI: Joi.string(),
MONGO_URI: Joi.string().allow(''),
DB_NAME: Joi.string(),
AZURE_STORAGE_CONNECTION_STRING: Joi.string(),
AZURE_STORAGE_LAYER_CONTAINER_NAME: Joi.string(),
Expand Down
7 changes: 7 additions & 0 deletions api/src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const DEFAULT_GET_LIMIT = 100;
const DEFAULT_GET_OFFSET = 0;

module.exports = {
DEFAULT_GET_LIMIT,
DEFAULT_GET_OFFSET,
};
87 changes: 87 additions & 0 deletions api/src/database/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const config = require('../config/config');
const APIError = require('../helpers/APIError');
const logger = require('../config/winston');
const { DEFAULT_GET_LIMIT, DEFAULT_GET_OFFSET } = require('../constants');

const mongoDb = require('./mongoDb/models/attributeModel');

const getLatestAttributes = () => {
if (config.mongoUri) {
return mongoDb.getLatestAttributes;
}
throw new APIError('No connection string to database', 500, false);
};

const getFilteredAttributes = () => {
if (config.mongoUri) {
return mongoDb.getFilteredAttributes;
}
throw new APIError('No connection string to database', 500, false);
};

const countAttributes = () => {
if (config.mongoUri) {
return mongoDb.countAttributes;
}
throw new APIError('No connection string to database', 500, false);
};
/**
* Get attributes from database collection
* @param {object} filters - object with keys for filtering
* @param {object} options - limit and offset for database query
*/
const getAttributes = async (filters, options) => {
let items = [];
let count = 0;
if (config.mongoUri) {
if (filters.latestValues) {
items = await getLatestAttributes()(filters.attributeId, filters.attributeIdCategory, filters.featureId).catch(
(e) => logger.error(`Error: ${e.message}`),
);
} else {
const dataLimit = Number.parseInt(options.limit, 10) || DEFAULT_GET_LIMIT;
const dataOffset = Number.parseInt(options.offset, 10) || DEFAULT_GET_OFFSET;
count = await countAttributes()(
filters.attributeId,
filters.attributeIdCategory,
filters.featureId,
filters.dateStart,
filters.dateEnd,
);
items = await getFilteredAttributes()(
filters.attributeId,
filters.attributeIdCategory,
filters.featureId,
filters.dateStart,
filters.dateEnd,
dataLimit,
dataOffset,
).catch((e) => logger.error(`Error: ${e.message}`));
}
}
return { items, count };
};

/**
* Returns all dates with data for given attributeId
* @param {string} attributeId
*/
const getAvailableDates = (attributeId) => {
if (config.mongoUri) {
return mongoDb.getAvailableDates(attributeId);
}
throw new APIError('No connection string to database', 500, false);
};

/**
* Returns unique featureIds for data stored for given attributeId
* @param {string} attributeId
*/
const getUniqueFeatureIds = (attributeId) => {
if (config.mongoUri) {
return mongoDb.getUniqueFeatureIds(attributeId);
}
throw new APIError('No connection string to database', 500, false);
};

module.exports = { getAttributes, getAvailableDates, getUniqueFeatureIds };
16 changes: 16 additions & 0 deletions api/src/database/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const config = require('../config/config');
const APIError = require('../helpers/APIError');

const mongoDb = require('./mongoDb');

/**
* Initializes database for which is provided connection string or throws error, when none found
*/
const initializeDb = () => {
if (config.mongoUri) {
return mongoDb.initializeDBConnection();
}
throw new APIError('No connection string to database', 500, false);
};

module.exports = { initializeDb };
16 changes: 16 additions & 0 deletions api/src/database/layers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const config = require('../config/config');
const APIError = require('../helpers/APIError');

const mongoDb = require('./mongoDb/models/layerModel');

/**
* Returns all map layers with correctly defined geographical data
*/
const getMapLayersWithGeoData = async () => {
if (config.mongoUri) {
return mongoDb.getMapLayersWithGeoData();
}
throw new APIError('No connection string to database', 500, false);
};

module.exports = { getMapLayersWithGeoData };
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* Add $geoIntersects filter for mongoose filter query
* @param {object} filter - existing filter object
* @param {string} bottomLeft - bottom left corner of boundingBox window, string 'lon,lat'
* @param {string} topRight - top right corner of boundingBox window, string 'lon,lat'
*/
const filterCoordinates = (filter, bottomLeft, topRight) => {
const bottomLeftArr = bottomLeft.split(',').map((point) => parseFloat(point));
const topRightArr = topRight.split(',').map((point) => parseFloat(point));
Expand Down
4 changes: 2 additions & 2 deletions api/src/db.js → api/src/database/mongoDb/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const mongoose = require('mongoose');
const config = require('./config/config');
const logger = require('./config/winston');
const config = require('../../config/config');
const logger = require('../../config/winston');

const initializeDBConnection = async () => {
logger.info(`Connecting to database`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const mongoose = require('mongoose');
const APIError = require('../helpers/APIError');
const APIError = require('../../../helpers/APIError');
const { ATTRIBUTES_COLLECTION_NAME } = require('../dbSchemas/attributeSchema');
const { dateIsValid } = require('../helpers/utils');
const { dateIsValid } = require('../../../helpers/utils');

/**
* Compose filter from settings from query
Expand All @@ -18,7 +18,7 @@ const createAttributesFilter = (attributeIds, attributeIdCategories, featureIds,
let attributeIdsArray = [];
let attributeCategoryArray = [];
if (attributeIds) {
attributeIdsArray = Array.isArray(attributeIds) ? attributeIds : attributeIds.split('');
attributeIdsArray = Array.isArray(attributeIds) ? attributeIds : [attributeIds];
filter.attributeId = { $in: attributeIdsArray };
}
if (attributeIdCategories) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
const model = require('../models/mapLayerModel');
const { getLayerGeoDataWithUrl } = require('../models/layerGeoDataModel');
const { MapLayer } = require('../dbSchemas/mapLayersSchema');
const LayerGeoDataSchema = require('../dbSchemas/layerGeoDataSchema');

const getMapLayers = async (filter) => {
const items = await MapLayer.find(filter).sort({ title: 'asc' }).lean().exec();
return items;
};

const getLayerGeoDataWithUrl = () =>
LayerGeoDataSchema.find({ geoDataUrl: { $ne: null } })
.lean()
.exec();

/**
* Returns map layers from mapLayers collection with link to GeoJson file from layerGeoData collection
Expand Down Expand Up @@ -33,7 +43,7 @@ const getMapLayersWithGeoData = async () => {
],
};

const layers = await model.getMapLayers(filter);
const layers = await getMapLayers(filter);

const layersWithGeoDataUrl = layers.map((layer) => {
if (layer.layerType === 'group') {
Expand Down Expand Up @@ -63,4 +73,4 @@ const getMapLayersWithGeoData = async () => {
return layersWithGeoDataUrl;
};

module.exports = { getMapLayersWithGeoData };
module.exports = { getMapLayers, getLayerGeoDataWithUrl, getMapLayersWithGeoData };
21 changes: 21 additions & 0 deletions api/src/database/mongoDb/models/pointAttributesModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const mongoose = require('mongoose');
const { POINT_ATTRIBUTES_COLLECTION } = require('../dbSchemas/pointAttributeSchema');
const { filterCoordinates } = require('../filters');

const getFilteredPointAttributes = async (attributeId, bottomLeft, topRight) => {
const { connection } = mongoose;
const { db } = connection;

let filter = {};
if (bottomLeft && topRight) {
filter = filterCoordinates(filter, bottomLeft, topRight);
}
if (attributeId) {
filter['properties.attributeId'] = attributeId;
}

const attributes = await db.collection(POINT_ATTRIBUTES_COLLECTION).find(filter).toArray();
return attributes;
};

module.exports = { getFilteredPointAttributes };
17 changes: 17 additions & 0 deletions api/src/database/pointAttributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const config = require('../config/config');
const APIError = require('../helpers/APIError');

const mongoDb = require('./mongoDb/models/pointAttributesModel');

/**
* @param {string} attributeId
* @param {string} bottomLeft - bottom left corner, string with lon,lat divided by ','
* @param {string} topRight - top right corner, string with lon, lat divided by ','
*/
const getPointAttributes = async (attributeId, bottomLeft, topRight) => {
if (config.mongoUri) {
return mongoDb.getFilteredPointAttributes(attributeId, bottomLeft, topRight);
}
throw new APIError('No connection string to database', 500, false);
};
module.exports = { getPointAttributes };
8 changes: 0 additions & 8 deletions api/src/models/layerGeoDataModel.js

This file was deleted.

8 changes: 0 additions & 8 deletions api/src/models/mapLayerModel.js

This file was deleted.

36 changes: 6 additions & 30 deletions api/src/routes/attributes.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,22 @@
const express = require('express');
const swaggerValidation = require('../config/swagger');
const { forwardError } = require('../helpers/utils');
const {
getLatestAttributes,
countAttributes,
getFilteredAttributes,
getAvailableDates,
getUniqueFeatureIds,
} = require('../models/attributeModel');
const logger = require('../config/winston');
const { getAttributes, getAvailableDates, getUniqueFeatureIds } = require('../database/attributes');

const router = express.Router();
const DEFAULT_LIMIT = 100;
const DEFAULT_OFFSET = 0;

router.get(
'/',
swaggerValidation.validate,
forwardError(async (req, res) => {
const { limit, offset, dateStart, dateEnd, attributeId, attributeIdCategory, featureId, latestValues } = req.query;

let items = [];
const { items, count } = await getAttributes(
{ attributeId, attributeIdCategory, featureId, dateStart, dateEnd, latestValues },
{ limit, offset },
);
res.set('X-Total-Count', count);

if (latestValues) {
items = await getLatestAttributes(attributeId, attributeIdCategory, featureId).catch((e) =>
logger.error(`Error: ${e.message}`),
);
} else {
const dataLimit = Number.parseInt(limit, 10) || DEFAULT_LIMIT;
const dataOffset = Number.parseInt(offset, 10) || DEFAULT_OFFSET;
const count = await countAttributes(attributeId, attributeIdCategory, featureId, dateStart, dateEnd);
res.set('X-Total-Count', count);
items = await getFilteredAttributes(
attributeId,
attributeIdCategory,
featureId,
dateStart,
dateEnd,
dataLimit,
dataOffset,
).catch((e) => logger.error(`Error: ${e.message}`));
}
res.send(items);
}),
);
Expand Down
4 changes: 2 additions & 2 deletions api/src/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ router.get(
try {
hasFile = fs.existsSync(path.join(__dirname, '..', '..', 'data', 'config', fileName));
} catch (err) {
logger.error(`Country configuration not found:\n${err}`);
logger.error(`UI configuration not found:\n${err}`);
return res.sendStatus(404);
}
if (hasFile) {
Expand All @@ -27,7 +27,7 @@ router.get(
);
res.send(countryConfig);
} else {
logger.error('Country configuration not found');
logger.error('UI configuration not found');
return res.sendStatus(404);
}
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const express = require('express');
const swaggerValidation = require('../config/swagger');
const { forwardError } = require('../helpers/utils');
const { getMapLayersWithGeoData } = require('../actions/mapLayersActions');
const { getMapLayersWithGeoData } = require('../database/layers');

const router = express.Router();

Expand Down
Loading