huridocs/uwazi

View on GitHub
app/api/suggestions/routes.ts

Summary

Maintainability
A
0 mins
Test Coverage
B
88%
/* eslint-disable max-lines */
import { Application, Request, Response } from 'express';
import { ObjectId } from 'mongodb';

import { Suggestions } from 'api/suggestions/suggestions';
import { InformationExtraction } from 'api/services/informationextraction/InformationExtraction';
import { validateAndCoerceRequest } from 'api/utils/validateRequest';
import { needsAuthorization } from 'api/auth';
import { parseQuery } from 'api/utils/parseQueryMiddleware';
import { ObjectIdSchema } from 'shared/types/commonTypes';
import { SuggestionsQueryFilterSchema } from 'shared/types/suggestionSchema';
import { objectIdSchema } from 'shared/types/commonSchemas';
import {
  IXAggregationQuery,
  IXSuggestionAggregation,
  IXSuggestionsQuery,
} from 'shared/types/suggestionType';
import { serviceMiddleware } from './serviceMiddleware';

const IX = new InformationExtraction();

async function processTrainFunction(
  callback: (extractorId: ObjectIdSchema) => Promise<{ message: string; status: string }>,
  req: Request,
  res: Response
) {
  if (!IX) {
    res.status(500).json({
      error: 'Information Extraction service is not available',
    });
    return;
  }

  const status = await callback(new ObjectId(req.body.extractorId));
  res.json(status);
}

function extractorIdRequestValidation(root = 'body') {
  return validateAndCoerceRequest({
    type: 'object',
    properties: {
      [root]: {
        type: 'object',
        additionalProperties: false,
        required: ['extractorId'],
        properties: {
          extractorId: { type: 'string' },
        },
      },
    },
  });
}

// eslint-disable-next-line max-statements
export const suggestionsRoutes = (app: Application) => {
  app.get(
    '/api/suggestions/',
    serviceMiddleware,
    needsAuthorization(['admin', 'editor']),
    parseQuery,
    validateAndCoerceRequest({
      type: 'object',
      properties: {
        query: {
          type: 'object',
          required: ['filter'],
          properties: {
            filter: SuggestionsQueryFilterSchema,
            page: {
              type: 'object',
              properties: {
                number: { type: 'number', minimum: 1 },
                size: { type: 'number', minimum: 1, maximum: 500 },
              },
            },
            sort: {
              type: 'object',
              properties: {
                property: { type: 'string' },
                order: { type: 'string' },
              },
            },
          },
        },
      },
    }),
    async (
      req: Request & {
        query: IXSuggestionsQuery;
      },
      res: Response
    ) => {
      const suggestionsList = await Suggestions.get(req.query.filter, {
        page: req.query.page,
        sort: req.query.sort,
      });
      res.json(suggestionsList);
    }
  );

  app.get(
    '/api/suggestions/aggregation',
    serviceMiddleware,
    needsAuthorization(['admin', 'editor']),
    parseQuery,
    validateAndCoerceRequest({
      type: 'object',
      definitions: { objectIdSchema },
      properties: {
        query: {
          type: 'object',
          additionalProperties: false,
          required: ['extractorId'],
          properties: {
            extractorId: objectIdSchema,
          },
        },
      },
    }),
    async (
      req: Request & {
        query: IXAggregationQuery;
      },
      res: Response<IXSuggestionAggregation>
    ) => {
      const { extractorId } = req.query;
      const aggregation = await Suggestions.aggregate(extractorId);
      res.json(aggregation);
    }
  );

  app.post(
    '/api/suggestions/stop',
    serviceMiddleware,
    needsAuthorization(['admin', 'editor']),
    extractorIdRequestValidation('body'),
    async (req, res, _next) => {
      await processTrainFunction(IX.stopModel, req, res);
    }
  );

  app.post(
    '/api/suggestions/train',
    serviceMiddleware,
    needsAuthorization(['admin', 'editor']),
    extractorIdRequestValidation('body'),
    async (req, res, _next) => {
      await processTrainFunction(IX.trainModel, req, res);
    }
  );

  app.post(
    '/api/suggestions/status',
    serviceMiddleware,
    needsAuthorization(['admin', 'editor']),
    extractorIdRequestValidation('body'),
    async (req, res, _next) => {
      await processTrainFunction(IX.status, req, res);
    }
  );

  app.post(
    '/api/suggestions/accept',
    serviceMiddleware,
    needsAuthorization(['admin', 'editor']),
    validateAndCoerceRequest({
      type: 'object',
      properties: {
        body: {
          type: 'object',
          additionalProperties: false,
          required: ['suggestions'],
          properties: {
            suggestions: {
              type: 'array',
              items: {
                type: 'object',
                additionalProperties: false,
                required: ['_id', 'sharedId', 'entityId'],
                properties: {
                  _id: objectIdSchema,
                  sharedId: { type: 'string' },
                  entityId: { type: 'string' },
                  addedValues: {
                    type: 'array',
                    items: { type: 'string' },
                  },
                  removedValues: {
                    type: 'array',
                    items: { type: 'string' },
                  },
                },
              },
            },
          },
        },
      },
    }),
    async (req, res, _next) => {
      const { suggestions } = req.body;
      await Suggestions.accept(suggestions);
      res.json({ success: true });
    }
  );
};