import type { HttpContext } from '@adonisjs/core/http'; import Dataset from '#models/dataset'; import { StatusCodes } from 'http-status-codes'; import DatasetReference from '#models/dataset_reference'; // node ace make:controller Author export default class DatasetController { /** * GET /api/datasets * Find all published datasets */ public async index({ response }: HttpContext) { try { const datasets = await Dataset.query() .where(function (query) { query.where('server_state', 'published').orWhere('server_state', 'deleted'); }) .preload('titles') .preload('identifier') .orderBy('server_date_published', 'desc'); return response.status(StatusCodes.OK).json(datasets); } catch (error) { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ message: error.message || 'Some error occurred while retrieving datasets.', }); } } /** * GET /api/dataset * Find all published datasets */ public async findAll({ response }: HttpContext) { try { const datasets = await Dataset.query() .where('server_state', 'published') .orWhere('server_state', 'deleted') .preload('descriptions') // Preload any relationships you need .orderBy('server_date_published'); return response.status(StatusCodes.OK).json(datasets); } catch (error) { return response.status(500).json({ message: error.message || 'Some error occurred while retrieving datasets.', }); } } /** * GET /api/dataset/:publish_id * Find one dataset by publish_id */ public async findOne({ response, params }: HttpContext) { try { const dataset = await Dataset.query() .where('publish_id', params.publish_id) .preload('titles') .preload('descriptions') // Using 'descriptions' instead of 'abstracts' .preload('user', (builder) => { builder.select(['id', 'firstName', 'lastName', 'avatar', 'login']); }) .preload('authors', (builder) => { builder .select(['id', 'academic_title', 'first_name', 'last_name', 'identifier_orcid', 'status', 'name_type']) .withCount('datasets', (query) => { query.as('datasets_count'); }) .pivotColumns(['role', 'sort_order']) .orderBy('pivot_sort_order', 'asc'); }) .preload('contributors', (builder) => { builder .select(['id', 'academic_title', 'first_name', 'last_name', 'identifier_orcid', 'status', 'name_type']) .withCount('datasets', (query) => { query.as('datasets_count'); }) .pivotColumns(['role', 'sort_order', 'contributor_type']) .orderBy('pivot_sort_order', 'asc'); }) .preload('subjects') .preload('coverage') .preload('licenses') .preload('references') .preload('project') // .preload('referenced_by', (builder) => { // builder.preload('dataset', (builder) => { // builder.preload('identifier'); // }); // }) .preload('files', (builder) => { builder.preload('hashvalues'); }) .preload('identifier') .first(); // Use first() instead of firstOrFail() to handle not found gracefully if (!dataset) { return response.status(StatusCodes.NOT_FOUND).json({ message: `Cannot find Dataset with publish_id=${params.publish_id}.`, }); } // Build the version chain const versionChain = await this.buildVersionChain(dataset); // Add version chain to response const responseData = { ...dataset.toJSON(), versionChain: versionChain, }; // return response.status(StatusCodes.OK).json(dataset); return response.status(StatusCodes.OK).json(responseData); } catch (error) { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ message: error.message || `Error retrieving Dataset with publish_id=${params.publish_id}.`, }); } } /** * GET /:prefix/:value * Find dataset by identifier (e.g., https://doi.tethys.at/10.24341/tethys.99.2) */ public async findByIdentifier({ response, params }: HttpContext) { const identifierValue = `${params.prefix}/${params.value}`; // Optional: Validate DOI format if (!identifierValue.match(/^10\.\d+\/[a-zA-Z0-9._-]+\.[0-9]+(?:\.[0-9]+)*$/)) { return response.status(StatusCodes.BAD_REQUEST).json({ message: `Invalid DOI format: ${identifierValue}`, }); } try { // Method 1: Using subquery with whereIn (most similar to your original) const dataset = await Dataset.query() // .whereIn('id', (subQuery) => { // subQuery.select('dataset_id').from('dataset_identifiers').where('value', identifierValue); // }) .whereHas('identifier', (builder) => { builder.where('value', identifierValue); }) .preload('titles') .preload('descriptions') // Using 'descriptions' instead of 'abstracts' .preload('user', (builder) => { builder.select(['id', 'firstName', 'lastName', 'avatar', 'login']); }) .preload('authors', (builder) => { builder .select(['id', 'academic_title', 'first_name', 'last_name', 'identifier_orcid', 'status', 'name_type']) .withCount('datasets', (query) => { query.as('datasets_count'); }) .pivotColumns(['role', 'sort_order']) .wherePivot('role', 'author') .orderBy('pivot_sort_order', 'asc'); }) .preload('contributors', (builder) => { builder .select(['id', 'academic_title', 'first_name', 'last_name', 'identifier_orcid', 'status', 'name_type']) .withCount('datasets', (query) => { query.as('datasets_count'); }) .pivotColumns(['role', 'sort_order', 'contributor_type']) .wherePivot('role', 'contributor') .orderBy('pivot_sort_order', 'asc'); }) .preload('subjects') .preload('coverage') .preload('licenses') .preload('references') .preload('project') // .preload('referenced_by', (builder) => { // builder.preload('dataset', (builder) => { // builder.preload('identifier'); // }); // }) .preload('files', (builder) => { builder.preload('hashvalues'); }) .preload('identifier') .first(); if (!dataset) { return response.status(StatusCodes.NOT_FOUND).json({ message: `Cannot find Dataset with identifier=${identifierValue}.`, }); } // Build the version chain const versionChain = await this.buildVersionChain(dataset); // Add version chain to response const responseData = { ...dataset.toJSON(), versionChain: versionChain, }; // return response.status(StatusCodes.OK).json(dataset); return response.status(StatusCodes.OK).json(responseData); } catch (error) { return response.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ message: error.message || `Error retrieving Dataset with identifier=${identifierValue}.`, }); } } /** * Build the complete version chain for a dataset * Traverses both backwards (previous versions) and forwards (newer versions) */ private async buildVersionChain(dataset: Dataset) { const versionChain = { current: { id: dataset.id, publish_id: dataset.publish_id, doi: dataset.identifier?.value || null, main_title: dataset.mainTitle || null, server_date_published: dataset.server_date_published, }, previousVersions: [] as any[], newerVersions: [] as any[], }; // Get all previous versions (going backwards in time) versionChain.previousVersions = await this.getPreviousVersions(dataset.id); // Get all newer versions (going forwards in time) versionChain.newerVersions = await this.getNewerVersions(dataset.id); return versionChain; } /** * Recursively get all previous versions */ private async getPreviousVersions(datasetId: number, visited: Set = new Set()): Promise { // Prevent infinite loops if (visited.has(datasetId)) { return []; } visited.add(datasetId); const previousVersions: any[] = []; // Find references where this dataset "IsNewVersionOf" another dataset const previousRefs = await DatasetReference.query() .where('document_id', datasetId) .where('relation', 'IsNewVersionOf') .whereNotNull('related_document_id'); for (const ref of previousRefs) { if (!ref.related_document_id) continue; const previousDataset = await Dataset.query() .where('id', ref.related_document_id) .preload('identifier') .preload('titles') .first(); if (previousDataset) { const versionInfo = { id: previousDataset.id, publish_id: previousDataset.publish_id, doi: previousDataset.identifier?.value || null, main_title: previousDataset.mainTitle || null, server_date_published: previousDataset.server_date_published, relation: 'IsPreviousVersionOf', // From perspective of current dataset }; previousVersions.push(versionInfo); // Recursively get even older versions const olderVersions = await this.getPreviousVersions(previousDataset.id, visited); previousVersions.push(...olderVersions); } } return previousVersions; } /** * Recursively get all newer versions */ private async getNewerVersions(datasetId: number, visited: Set = new Set()): Promise { // Prevent infinite loops if (visited.has(datasetId)) { return []; } visited.add(datasetId); const newerVersions: any[] = []; // Find references where this dataset "IsPreviousVersionOf" another dataset const newerRefs = await DatasetReference.query() .where('document_id', datasetId) .where('relation', 'IsPreviousVersionOf') .whereNotNull('related_document_id'); for (const ref of newerRefs) { if (!ref.related_document_id) continue; const newerDataset = await Dataset.query().where('id', ref.related_document_id).preload('identifier').preload('titles').first(); if (newerDataset) { const versionInfo = { id: newerDataset.id, publish_id: newerDataset.publish_id, doi: newerDataset.identifier?.value || null, main_title: newerDataset.mainTitle || null, server_date_published: newerDataset.server_date_published, relation: 'IsNewVersionOf', // From perspective of current dataset }; newerVersions.push(versionInfo); // Recursively get even newer versions const evenNewerVersions = await this.getNewerVersions(newerDataset.id, visited); newerVersions.push(...evenNewerVersions); } } return newerVersions; } }