huridocs/uwazi

View on GitHub
app/react/Markdown/markdownDatasets.js

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
import { Parser as HTMLParser } from 'htmlparser2';
import qs from 'qs';
import { risonDecodeOrIgnore } from 'app/utils';
import Big from 'big.js';

import searchApi from 'app/Search/SearchAPI';
import api from 'app/utils/api';
import entitiesApi from 'app/Entities/EntitiesAPI';

const conformUrl = ({ url = '', geolocation = false }) => {
  const { q } = qs.parse(url.substring(url.indexOf('?')), { ignoreQueryPrefix: true });

  if (!q) {
    const defaultValue = geolocation
      ? { allAggregations: true, limit: 0, geolocation: true }
      : { allAggregations: true, limit: 0 };

    return defaultValue;
  }

  const params = risonDecodeOrIgnore(q);
  params.limit = 0;

  if (geolocation) {
    params.geolocation = true;
  }

  return params;
};

const conformValues = attribs => (attribs.entity ? attribs : conformUrl(attribs));

const parseDatasets = markdown => {
  const result = {};
  const parser = new HTMLParser(
    {
      onopentag(name, attribs) {
        if (name === 'dataset') {
          result[attribs.name || 'default'] = conformValues(attribs);
        }
        if (name === 'query') {
          result[attribs.name || 'default'] = { url: attribs.url, query: true };
        }
      },
    },
    { decodeEntities: true }
  );

  parser.parseComplete(markdown);
  return result;
};

const requestDatasets = (datasets, requestParams) =>
  Promise.all(
    Object.keys(datasets).map(name => {
      if (datasets[name].query) {
        return api
          .get(datasets[name].url, requestParams)
          .then(data => ({ data: data.json, name, params: datasets[name] }));
      }
      const apiAction = datasets[name].entity ? entitiesApi.get : searchApi.search;
      const params = datasets[name].entity ? { sharedId: datasets[name].entity } : datasets[name];
      const postAction = datasets[name].entity ? d => d[0] : d => d;
      return apiAction(requestParams.set(params))
        .then(postAction)
        .then(data => ({ data, name, params: datasets[name] }));
    })
  );

const conformDatasets = sets =>
  sets.reduce((memo, set) => {
    const data = set.params.extractFirstRow ? set.data.rows[0] : set.data;
    return { ...memo, [set.name]: data };
  }, {});

const getAggregations = (state, { property, dataset = 'default' }) => {
  const data = state.page.datasets.get(dataset);
  return !data ? undefined : data.getIn(['aggregations', 'all', property, 'buckets']);
};

const addValues = (aggregations, values) => {
  let result = new Big(0);
  values.forEach(key => {
    const value = aggregations.find(bucket => bucket.get('key') === key);
    const filteredValue = value ? value.getIn(['filtered', 'doc_count']) : 0;
    result = result.plus(filteredValue || 0);
  });
  return Number(result);
};

export default {
  async fetch(markdown, requestParams, { additionalDatasets = {} } = {}) {
    const datasets = parseDatasets(markdown);
    const extendedDatsets = { ...datasets, ...additionalDatasets };
    return requestDatasets(extendedDatsets, requestParams).then(conformDatasets);
  },

  getRows(state, { dataset = 'default' }) {
    const data = state.page.datasets.get(dataset);
    if (!data) {
      return undefined;
    }
    return data.get('rows');
  },

  getAggregations,

  getAggregation(state, { uniqueValues, property, value, dataset = 'default' }) {
    const aggregations = getAggregations(state, { property, dataset });
    if (!aggregations) {
      return undefined;
    }

    if (uniqueValues) {
      return aggregations.filter(a => a.getIn(['filtered', 'doc_count']) !== 0).size;
    }

    const values = value ? value.split(',') : [''];
    return addValues(aggregations, values);
  },

  getMetadataValue(state, { property, dataset = 'default' }) {
    const data = state.page.datasets.get(dataset);
    const propertyExists = data && data.hasIn(['metadata', property]);
    const mos = propertyExists ? data.getIn(['metadata', property]).toJS() : [];
    return mos && mos.length && mos[0].value ? Number(mos[0].value) : undefined;
  },
};