Skip to content
Open
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
15 changes: 14 additions & 1 deletion .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,30 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
cache: npm
cache-dependency-path: package-lock.json
cache-dependency-path: |
package-lock.json
server/package-lock.json

- name: Install app dependencies
run: npm ci

- name: Install server dependencies
run: npm ci
working-directory: server

- name: Type check
run: npm run type-check

- name: Lint TypeScript
run: npm run lint-ts-test

- name: Lint SCSS
run: npm run lint-scss-test

- name: Lint server
run: npm run lint-check
working-directory: server

- name: Unit tests
run: npm test -- --ci

Expand Down
11 changes: 4 additions & 7 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,10 @@ module.exports = api => {
presets: [
[
'@babel/preset-env',
isTest
? { targets: { node: 'current' } }
: {
targets: {
browsers: ['>0.2%', 'not dead', 'not ie <= 11', 'not op_mini all'],
},
},
// In test we compile for the running Node version. Otherwise pass no
// explicit targets so preset-env reads the `browserslist` field in
// package.json — the single source of truth for build targets.
isTest ? { targets: { node: 'current' } } : {},
],
// runtime: 'automatic' enables the new JSX transform — no need to
// import React in every file
Expand Down
18 changes: 15 additions & 3 deletions server/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"root": true,
"env": {
"commonjs": true,
"es6": true,
Expand All @@ -16,11 +17,22 @@
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
"ecmaVersion": 2021,
"sourceType": "module"
},
"rules": {
"prettier/prettier": [
"error"
]
"error",
{
"arrowParens": "avoid",
"endOfLine": "auto",
"singleQuote": true,
"trailingComma": "all"
}
],
"import/extensions": "off",
"no-use-before-define": "off",
"no-underscore-dangle": "off",
"no-param-reassign": "off"
}
}
31 changes: 17 additions & 14 deletions server/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import express from 'express';
import path from 'path';
import logger from 'morgan';
import axios from './axios-general-instance-config.js';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import { fileURLToPath } from 'url';
import axios from './axios-general-instance-config.js';
import setUpOIDC, {
userAuthenticated,
errorHandler,
Expand All @@ -21,13 +21,15 @@ import {
import axiosPoolForApi from './axios-api-instance-config.js';

const baseurl = process.env.API_URL ?? 'https://dev-api.digitransit.fi';
const FavouriteHost = process.env.FAVOURITE_HOST || 'https://dev-api.digitransit.fi/favourites';
const FavouriteHost =
process.env.FAVOURITE_HOST || 'https://dev-api.digitransit.fi/favourites';

const NotificationHost =
process.env.NOTIFICATION_HOST ||
'https://test.hslfi.hsldev.com/user/api/v1/notifications';

const apiSubscriptionParameter = process.env.API_SUBSCRIPTION_QUERY_PARAMETER_NAME
const apiSubscriptionParameter = process.env
.API_SUBSCRIPTION_QUERY_PARAMETER_NAME
? `${process.env.API_SUBSCRIPTION_QUERY_PARAMETER_NAME}=${process.env.API_SUBSCRIPTION_TOKEN}`
: '';

Expand Down Expand Up @@ -84,14 +86,13 @@ app.get('/api/geocoding/:endpoint', (req, res, next) => {
});

app.get('/api/map/:lang', (req, res) => {
const { lang } = req.params
const { lang } = req.params;
let url;
const hasLang = lang !== 'null' && lang !== 'undefined'
if(hasLang && lang !== 'fi') {
url = `${MAP_URL}/hsl-map-${lang}/{z}/\{x}/{y}.png?${apiSubscriptionParameter}`
} else {
url = `${MAP_URL}/hsl-map/{z}/\{x}/{y}.png?${apiSubscriptionParameter}`

const hasLang = lang !== 'null' && lang !== 'undefined';
if (hasLang && lang !== 'fi') {
url = `${MAP_URL}/hsl-map-${lang}/{z}/{x}/{y}.png?${apiSubscriptionParameter}`;
} else {
url = `${MAP_URL}/hsl-map/{z}/{x}/{y}.png?${apiSubscriptionParameter}`;
}
return res.status(200).json(url);
});
Expand Down Expand Up @@ -148,7 +149,7 @@ app.post('/api/staticmonitor', userAuthenticated, (req, res, next) => {

app.put('/api/staticmonitor', userAuthenticated, (req, res, next) => {
createMonitor(req, res, next)
.then(response => {
.then(() => {
monitorService.createStatic(req, res, next);
})
.catch(err => {
Expand All @@ -161,7 +162,8 @@ app.get(
userAuthenticated,
(req, res, next) => {
getMonitors(req, res, next);
});
},
);

app.get('/api/userowned/:id', userAuthenticated, (req, res, next) => {
isUserOwnedMonitor(req, res, next);
Expand Down Expand Up @@ -227,16 +229,17 @@ app.use((req, res, next) => {
});

// error handler
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

console.error(
`Request error (${err.response?.status}): ${err.message}
url: ${req.url},
operation: ${req.body?.operationName}
response: (${err.response?.status}) ${err.response?.statusText}`
response: (${err.response?.status}) ${err.response?.statusText}`,
);

// render the error page
Expand Down
4 changes: 2 additions & 2 deletions server/axios-api-instance-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Dedicated pool for API requests, which are constant and frequent. */
import axiosInstance from "axios";
import { HttpsAgent } from "agentkeepalive";
import axiosInstance from 'axios';
import { HttpsAgent } from 'agentkeepalive';

const httpsKeepaliveAgent = new HttpsAgent({
maxSockets: 128, // max SNAT ports per endpoint is 128
Expand Down
4 changes: 2 additions & 2 deletions server/axios-general-instance-config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* Shared pool for requests other than GraphQL. */
import axiosInstance from "axios";
import { HttpsAgent } from "agentkeepalive";
import axiosInstance from 'axios';
import { HttpsAgent } from 'agentkeepalive';

const httpsKeepaliveAgent = new HttpsAgent({
maxSockets: 1000,
Expand Down
2 changes: 1 addition & 1 deletion server/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function parseEnvPropJSON(envProperty, envPropertyName) {
const propertyJson = JSON.parse(envProperty);
if (typeof propertyJson !== 'object') {
throw new Error(
`Property ${envPropertyName} is not an object. Expected a JSON object.`
`Property ${envPropertyName} is not an object. Expected a JSON object.`,
);
}
return propertyJson;
Expand Down
33 changes: 21 additions & 12 deletions server/monitorService.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ async function getMonitor(hash) {
.fetchAll();
return items;
} catch (e) {
console.error(`Failed to fetch monitor ${hash}: ${e.message}`);
throw e;
}
}
Expand All @@ -46,11 +47,11 @@ const monitorService = {
try {
const cont = database.container('staticMonitors');
const urls = ids;
const instanceName = req.params.instanceName;
// query to return all items
if (urls.length) {
const querySpec = {
query: 'SELECT * from c WHERE ARRAY_CONTAINS(@urls, c.url) AND (IS_DEFINED(c.instance) = false OR c.instance = @instance)',
query:
'SELECT * from c WHERE ARRAY_CONTAINS(@urls, c.url) AND (IS_DEFINED(c.instance) = false OR c.instance = @instance)',
parameters: [
{
name: '@urls',
Expand Down Expand Up @@ -88,7 +89,7 @@ const monitorService = {
},
getStatic: async function getStaticMonitor(req, res, next) {
try {
const container = database.container('staticMonitors');
const staticContainer = database.container('staticMonitors');
const url = req.params.id;
const querySpec = {
query: 'SELECT * from c WHERE c.url = @url',
Expand All @@ -99,7 +100,7 @@ const monitorService = {
},
],
};
const { resources: items } = await container.items
const { resources: items } = await staticContainer.items
.query(querySpec)
.fetchAll();
if (items.length) {
Expand All @@ -113,30 +114,38 @@ const monitorService = {
},
createStatic: async function createStaticMonitor(req, res, next) {
try {
const container = database.container('staticMonitors');
await Promise.resolve(container.items.create(req.body));
const staticContainer = database.container('staticMonitors');
await Promise.resolve(staticContainer.items.create(req.body));
res.send('OK');
} catch (e) {
next(e);
}
},
updateStatic: async function updateStaticMonitor(req, res) {
updateStatic: async function updateStaticMonitor(req, res, next) {
try {
const container = database.container('staticMonitors');
const { resource: updatedItem } = await container
const staticContainer = database.container('staticMonitors');
const { resource: updatedItem } = await staticContainer
.item(req.body.id, req.body.url)
.replace(req.body);
res.json(updatedItem);
} catch (e) {
throw e;
console.error(
`Failed to update static monitor ${req.body?.url}: ${e.message}`,
);
next(e);
}
},
deleteStatic: async function deleteStaticMonitor(req, res) {
try {
const container = database.container('staticMonitors');
const { body } = await container.item(req.body.id, req.body.url).delete();
const staticContainer = database.container('staticMonitors');
const { body } = await staticContainer
.item(req.body.id, req.body.url)
.delete();
res.status(200).json(body);
} catch (e) {
console.error(
`Failed to delete static monitor ${req.body?.url}: ${e.message}`,
);
throw e;
}
},
Expand Down
Loading