Skip to content

Commit

Permalink
feat: create kennisbank-api server
Browse files Browse the repository at this point in the history
  • Loading branch information
AliKdhim87 committed Oct 1, 2024
1 parent 77e15ac commit 4283914
Show file tree
Hide file tree
Showing 15 changed files with 600 additions and 279 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: [
'./apps/kennisbank-api/tsconfig.json',
'./apps/kennisbank-dashboard/src/admin/tsconfig.json',
'./apps/kennisbank-dashboard/tsconfig.json',
'./apps/kennisbank-frontend/tsconfig.json',
Expand Down
32 changes: 32 additions & 0 deletions apps/kennisbank-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@frameless/kennisbank-api",
"version": "0.0.0",
"private": true,
"author": "@frameless",
"description": "Kennisbank API for PDC",
"license": "EUPL-1.2",
"keywords": [],
"scripts": {
"prebuild": "yarn clean",
"build": "tsc -p ./tsconfig.json",
"watch": "tsc -p ./tsconfig.json -w",
"start": "node dist/server.js",
"dev": "nodemon src/server.ts",
"clean": "rimraf dist",
"lint-build": "tsc --noEmit --project tsconfig.json"
},
"dependencies": {
"dotenv": "16.4.5",
"express": "4.21.0"
},
"devDependencies": {
"nodemon": "3.1.7",
"rimraf": "6.0.1",
"typescript": "5.0.4"
},
"repository": {
"type": "git+ssh",
"url": "git@github.com:frameless/strapi.git",
"directory": "apps/kennisbank-api"
}
}
1 change: 1 addition & 0 deletions apps/kennisbank-api/src/controllers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { kennisartikelController } from './kennisartikel';
88 changes: 88 additions & 0 deletions apps/kennisbank-api/src/controllers/kennisartikel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import type { RequestHandler } from 'express';
import { SEARCH_PRODUCT } from '../../queries';
import { fetchData } from '../../utils';

export const kennisartikelController: RequestHandler = async (req, res, next) => {
const locale = (req.query?.locale as string) || 'nl';
const query = req.query?.query as string;
const graphqlURL = new URL('/graphql', process.env.STRAPI_PRIVATE_URL);
try {
const { data } = await fetchData<any>({
url: graphqlURL.href,
query: SEARCH_PRODUCT,
variables: { locale, query },
headers: {
Authorization: `Bearer ${process.env.PDC_STRAPI_API_TOKEN}`,
},
});

res.set('Content-Type', 'application/json');
if (data && data.products && data.products?.data.length > 0) {
const productAttributes = data.products.data[0].attributes;
res.status(200);
res.set('Cache-control', 'public, s-maxage=86400, stale-while-revalidate');
const upnUri = new URL(`products/${productAttributes.slug}`, process.env.FRONTEND_PUBLIC_URL);
const contentBlock = productAttributes?.sections.find(
({ component }: any) => component === 'ComponentComponentsUtrechtRichText',
);
const metaTags = productAttributes?.metaTags;
const trefwoorden = metaTags?.keymatch?.split(', ').map((trefwoord: string) => ({ trefwoord })) || [];

const kannisartikel_schema = {
url: productAttributes.slug,
uuid: data.products?.data[0].uuid,
upnUri: upnUri.href,
publicatieDatum: productAttributes.createdAt,
productAanwezig: true,
productValtOnder: null, // we need an extra field for this
// verantwoordelijkeOrganisatie: {
// url: '',
// owmsIdentifier: '',
// owmsPrefLabel: '',
// owmsEndDate: '2023-12-31T23:59:59Z'
// },
locaties: null, //Een lijst met locaties waarop dit product beschikbaar is. Deze is nog niet nodig voor KISS en mag null zijn. Dit obecjt is dus nog niet opgenomen in dit schema
doelgroep: productAttributes?.catalogiMeta?.audience[0].type, //TODO: We need to verify whether a product in the Strapi dashboard can have multiple values stored as an array.
// afdelingen: [
// {
// afdelingId: '',
// afdelingnaam: ''
// }
// ],
beschikbareTalen: '',
vertalingen: [
{
taal: productAttributes?.locale,
titel: productAttributes?.title,

contact: contentBlock?.kennisartikelCategorie === 'contact' ? contentBlock.content : undefined,
deskMemo: contentBlock?.kennisartikelCategorie === 'deskMemo' ? contentBlock.content : undefined,
tekst: contentBlock?.kennisartikelCategorie === 'inleiding' ? contentBlock.content : undefined,
procedureBeschrijving:
contentBlock?.kennisartikelCategorie === 'aanvraag' ? contentBlock.content : undefined,
bewijs: contentBlock?.kennisartikelCategorie === 'bewijs' ? contentBlock.content : undefined,
vereisten: contentBlock?.kennisartikelCategorie === 'voorwaarden' ? contentBlock.content : undefined,
bezwaarEnBeroep: contentBlock?.kennisartikelCategorie === 'bezwaar' ? contentBlock.content : undefined,
kostenEnBetaalmethoden:
contentBlock?.kennisartikelCategorie === 'kosten' ? contentBlock.content : undefined,
uitersteTermijn: contentBlock?.kennisartikelCategorie === 'termijn' ? contentBlock.content : undefined,
wtdBijGeenReactie:
contentBlock?.kennisartikelCategorie === 'wat_te_doen_bij_geen_reactie'
? contentBlock.content
: undefined,
notice: contentBlock?.kennisartikelCategorie === 'bijzonderheden' ? contentBlock.content : undefined,
trefwoorden,
datumWijziging: productAttributes.updatedAt,
},
],
};
return res.send(kannisartikel_schema);
}
res.status(200);

return res.send([]);
} catch (error) {
next(error);
return null;
}
};
52 changes: 52 additions & 0 deletions apps/kennisbank-api/src/queries/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const gql = (query: any) => query;
export const SEARCH_PRODUCT = gql(`
query searchProduct($locale: I18NLocaleCode, $query: String) {
products(locale: $locale, pagination: {start: 0, limit: -1}, filters: { title: {containsi: $query}}) {
data {
id
attributes {
title
slug
locale
updatedAt
createdAt
locale
metaTags {
keymatch
title
description
}
sections {
... on ComponentComponentsUtrechtRichText {
id
content
kennisartikelCategorie
component:__typename
}
}
catalogiMeta {
abstract
spatial {
scheme
resourceIdentifier
}
authority {
scheme
resourceIdentifier
}
audience {
id
type
}
onlineRequest {
type
}
}
pdc_metadata {
uplProductNaam
}
}
}
}
}
`);
1 change: 1 addition & 0 deletions apps/kennisbank-api/src/routers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as kennisartikel } from './kennisartikel';
8 changes: 8 additions & 0 deletions apps/kennisbank-api/src/routers/kennisartikel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express from 'express';
import { kennisartikelController } from '../../controllers';

const router = express.Router({ mergeParams: true });

router.get('/kennisartikel', kennisartikelController);

export default router;
48 changes: 48 additions & 0 deletions apps/kennisbank-api/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { config } from 'dotenv';
import express from 'express';
import { NextFunction, Request, Response } from 'express';
import { kennisartikel } from './routers';
import { envAvailability, ErrorHandler } from './utils';

config();
// Validate environment variables
envAvailability({
env: process.env,
keys: ['STRAPI_PRIVATE_URL', 'PDC_STRAPI_API_TOKEN', 'FRONTEND_PUBLIC_URL', 'KENNIS_BANK_API_PORT'],
});

const app = express();

const port = process.env.KENNIS_BANK_API_PORT;
// Centralized error handler middleware
const globalErrorHandler = (err: ErrorHandler, _req: Request, res: Response, _next: NextFunction) => {
if (err instanceof ErrorHandler || (err as ErrorHandler)?.isOperational) {
// Send the proper error response with status code and message
return res.status(err?.options?.statusCode || 500).json({
message: err.message,
});
}

// If it's an unknown error (not an operational error), log it and send a generic response
// eslint-disable-next-line no-console
console.error('Unexpected error:', err);

return res.status(500).json({
message: 'An unexpected error occurred.',
});
};
// Use global error handler middleware
app.use(globalErrorHandler);
/**
* Routes
* /api/kennisartikel
*/
app.use('/api', kennisartikel);
// handle non existing routes
app.use((_req, res) => {
res.status(404).send('Route not found');
});
app.listen(port, () => {
// eslint-disable-next-line no-console
console.log(`Kennesbank app listening on port ${port}!`);
});
11 changes: 11 additions & 0 deletions apps/kennisbank-api/src/utils/envAvailability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
interface EnvValidator {
env: any;
keys: string[];
}
export const envAvailability = ({ env, keys }: EnvValidator) => {
keys?.forEach((key: string) => {
if (!env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
});
};
16 changes: 16 additions & 0 deletions apps/kennisbank-api/src/utils/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type Options = {
statusCode: number;
};
export class ErrorHandler extends Error {
isOperational: boolean; // this flag for custom error identification

constructor(
message?: string,
public options?: Options,
) {
super(message);
this.name = 'ErrorHandler';
this.options = options;
this.isOperational = true; // Operational errors should be marked
}
}
Loading

0 comments on commit 4283914

Please sign in to comment.