Some checks failed
build.yaml / feat: Enhance background job settings UI and functionality (push) Failing after 0s
- Updated BackgroundJob.vue to improve the display of background job statuses, including missing cross-references and current job mode. - Added auto-refresh functionality for background job status. - Introduced success toast notifications for successful status refreshes. - Modified the XML serialization process in DatasetXmlSerializer for better caching and performance. - Implemented a new RuleProvider for managing custom validation rules. - Improved error handling in routes for loading background job settings. - Enhanced ClamScan configuration with socket support for virus scanning. - Refactored dayjs utility to streamline locale management.
324 lines
13 KiB
TypeScript
324 lines
13 KiB
TypeScript
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<number> = new Set()): Promise<any[]> {
|
|
// 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<number> = new Set()): Promise<any[]> {
|
|
// 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;
|
|
}
|
|
}
|