digitalfabrik/integreat-app

View on GitHub
shared/api/endpoints/testing/CategoriesMapModelBuilder.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import { DateTime } from 'luxon'
import md5 from 'md5'
import seedrandom from 'seedrandom'

import { APPOINTMENT_BOOKING_OFFER_ALIAS } from '../../../constants'
import CategoriesMapModel from '../../models/CategoriesMapModel'
import CategoryModel from '../../models/CategoryModel'
import OfferModel from '../../models/OfferModel'
import OrganizationModel from '../../models/OrganizationModel'

type PageResourceCacheEntryStateType = {
  readonly filePath: string
  readonly lastUpdate: DateTime
  readonly hash: string
}
type PageResourceCacheStateType = Record<string, PageResourceCacheEntryStateType>
const DEFAULT_ARITY = 3
const DEFAULT_DEPTH = 2
const MAX_PREDICTABLE_VALUE = 6

/**
 * This builder generates a perfect m-ary tree of categories with the specified depth.
 */

class CategoriesMapModelBuilder {
  _depth: number
  _arity: number
  _city: string
  _language: string
  _categories: Array<CategoryModel> = []
  _resourceCache: Record<string, PageResourceCacheStateType> = {}
  _id = 0

  constructor(city: string, language: string, arity: number = DEFAULT_ARITY, depth: number = DEFAULT_DEPTH) {
    this._arity = arity
    this._depth = depth
    this._city = city
    this._language = language
    this._categories = []
    this._resourceCache = {}
  }

  _predictableNumber(index: number, max: number = MAX_PREDICTABLE_VALUE): number {
    return seedrandom(`${index}-seed`)() * max
  }

  createResource(url: string, index: number, lastUpdate: DateTime): PageResourceCacheEntryStateType {
    const hash = md5(url)
    return {
      filePath: `path/to/documentDir/resource-cache/v1/${this._city}/files/${hash}.png`,
      lastUpdate: lastUpdate.plus({ days: this._predictableNumber(index) }),
      hash,
    }
  }

  _addChildren(category: CategoryModel, depth: number): void {
    this._categories.push(category)

    if (depth === 0) {
      this._resourceCache[category.path] = {}
    }

    if (depth === this._depth) {
      return
    }

    for (let i = 0; i < this._arity; i += 1) {
      const id = this._id
      this._id += 1
      const path = `${category.path}/category_${i}`
      const lastUpdate = DateTime.fromISO('2017-11-18T19:30:00.000Z')
      const resourceUrl1 = `https://cms.integreat-app.de/title_${id}-300x300.png`
      const resourceUrl2 = `https://cms.integreat-app.de/category_${id}-300x300.png`
      const thumbnail = `https://cms.integreat-app.de/thumbnails/category_${id}.png`
      const newChild = new CategoryModel({
        root: false,
        path,
        title: `Category with id ${id}`,
        content: `<h1>Page of Category ${id}</h1>
                    <img src="${resourceUrl1}" alt="" />
                    <p>This is a sample page</p>
                    <img src="${resourceUrl2}" alt=""/>`,
        order: i,
        availableLanguages: {},
        thumbnail,
        parentPath: category.path,
        lastUpdate,
        organization: new OrganizationModel({
          name: 'Tür an Tür',
          logo: 'https://example.com/my-icon',
          url: 'https://example.com',
        }),
        embeddedOffers:
          id === 1
            ? [
                new OfferModel({
                  alias: 'sprungbrett',
                  thumbnail: 'some_other_thumbnail',
                  title: 'Sprungbrett',
                  path: 'https://web.integreat-app.de/proxy/sprungbrett/app-search-internships?location=augsburg',
                }),
                new OfferModel({
                  alias: APPOINTMENT_BOOKING_OFFER_ALIAS,
                  thumbnail: 'some_other_thumbnail',
                  title: 'Terminbuchung',
                  path: 'https://termine.malteapp.de/',
                }),
              ]
            : [],
      })
      this._resourceCache[path] = {
        [resourceUrl1]: this.createResource(resourceUrl1, id, lastUpdate),
        [resourceUrl2]: this.createResource(resourceUrl2, id, lastUpdate),
        [thumbnail]: this.createResource(thumbnail, id, lastUpdate),
      }

      this._addChildren(newChild, depth + 1)
    }
  }

  buildResources(): Record<string, PageResourceCacheStateType> {
    return this.buildAll().resourceCache
  }

  build(): CategoriesMapModel {
    return this.buildAll().categories
  }

  buildAll(): {
    categories: CategoriesMapModel
    resourceCache: Record<string, PageResourceCacheStateType>
  } {
    this._resourceCache = {}
    this._categories = []
    this._id = 0
    const path = `/${this._city}/${this._language}`

    this._addChildren(
      new CategoryModel({
        root: true,
        path,
        title: `${this._city}`,
        content: '',
        order: -1,
        availableLanguages: {},
        thumbnail: '',
        parentPath: '',
        lastUpdate: DateTime.fromISO('2017-11-18T19:30:00.000Z'),
        organization: null,
        embeddedOffers: [],
      }),
      0,
    )

    return {
      categories: new CategoriesMapModel(this._categories),
      resourceCache: this._resourceCache,
    }
  }
}

export default CategoriesMapModelBuilder