import knex from './knex.js'; import redis from './redis.js'; import initLogger from './logger.js'; import entityPrefixes from './entities-prefixes.js'; const logger = initLogger(); export function curateEntity(entity, context) { if (!entity) { return null; } return { id: entity.id, name: entity.name, slug: entity.slug, type: entity.type, url: entity.url, isIndependent: entity.independent, hasLogo: entity.has_logo, parent: curateEntity(entity.parent, context), children: context?.children?.filter((child) => child.parent_id === entity.id).map((child) => curateEntity({ ...child, parent: entity }, { parent: entity })) || [], affiliate: entity.affiliate ? { id: entity.affiliate.id, url: entity.affiliate.url, parameters: entity.affiliate.parameters, } : null, ...context?.append?.[entity.id], }; } export async function fetchEntities(options = {}) { const entities = await knex('entities') .select('entities.*', knex.raw('row_to_json(parents) as parent')) .modify((builder) => { if (options.query) { builder.where((whereBuilder) => { whereBuilder .where((subBuilder) => { subBuilder .whereILike('entities.name', `%${options.query}%`) .orWhereILike('entities.slug', `%${options.query}%`); }) .whereNot('entities.type', 'info'); }); } if (options.type === 'primary') { builder .where((subBuilder) => { subBuilder .where('entities.type', 'network') .orWhere('entities.independent', true) .orWhereNull('entities.parent_id'); }) .whereNot('entities.type', 'info'); return; } if (options.type) { builder.where('entities.type', options.type); } if (options.showInvisible !== true) { builder.where('entities.visible', true); } }) .leftJoin('entities as parents', 'parents.id', 'entities.parent_id') .orderBy(...(options.order || ['name', 'asc'])) .offset((options.page - 1) * options.limit) .limit(options.limit || 1000); return entities.map((entityEntry) => curateEntity(entityEntry)); } export async function fetchEntitiesById(entityIds, options = {}) { const [entities, children] = await Promise.all([ knex('entities') .select( 'entities.*', knex.raw('row_to_json(parents) as parent'), knex.raw('row_to_json(affiliates) as affiliate'), ) .whereIn('entities.id', entityIds) .leftJoin('entities as parents', 'parents.id', 'entities.parent_id') .leftJoin('affiliates', knex.raw('affiliates.entity_id in (entities.id, parents.id)')) .modify((builder) => { if (options.order) { builder.orderBy(...options.order); } }) .groupBy('entities.id', 'parents.id', 'affiliates.id'), options.includeChildren ? knex('entities') .whereIn('entities.parent_id', entityIds) .whereNot('type', 'info') .orderBy('slug') : [], ]); if (options.order) { return entities.map((entityEntry) => curateEntity(entityEntry, { append: options.append, children, })); } const curatedEntities = entityIds.map((entityId) => { const entity = entities.find((entityEntry) => entityEntry.id === entityId); if (!entity) { console.warn(`Can't match entity ${entityId}`); return null; } return curateEntity(entity, { append: options.append, children, }); }).filter(Boolean); return curatedEntities; } export async function getEntityIdsBySlug(slugs, domain) { const ids = await Promise.all(slugs.map(async (slug) => { if (!slug) { return null; } if (Number(slug)) { return Number(slug); // already an ID or missing } const id = await redis.hGet(`traxxx:${domain}:id_by_slug`, slug); return Number(id); })); return ids.filter(Boolean); } export async function cacheEntityIds() { const entities = await knex('entities').select('id', 'slug', 'type'); await redis.del('traxxx:entities:id_by_slug'); await redis.hSet('traxxx:entities:id_by_slug', entities.map((entity) => [`${entityPrefixes[entity.type]}${entity.slug}`, entity.id])); logger.info('Cached entity IDs by slug'); }