diff --git a/.github/workflows/deploy-main.yml b/.github/workflows/deploy-main.yml index 50fc6593..6e2b1903 100644 --- a/.github/workflows/deploy-main.yml +++ b/.github/workflows/deploy-main.yml @@ -227,4 +227,4 @@ jobs: - run: yarn prisma:generate - - run: yarn prisma:migrate:deploy + - run: yarn prisma:migrate:deploy > /dev/null diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index ab2de0fb..511958d6 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -227,4 +227,4 @@ jobs: - run: yarn prisma:generate - - run: yarn prisma:migrate:deploy + - run: yarn prisma:migrate:deploy > /dev/null diff --git a/CHANGELOG.md b/CHANGELOG.md index 5132d5a5..ab561235 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Update CI and deployment workflows to include new environment variables 'BOT_LOGWATCH_URL' and 'VALIDATOR_URL'. - Patch to Zenodo workflow that was causing the user to be notified of a failed Zenodo upload when the upload was successful. +- Fix issue with the metadata validation endpoint that was causing the service to return a 405 error. ## v3.2.0 - 12-10-2024 diff --git a/CITATION.cff b/CITATION.cff index 9c93aa58..c7e9bf78 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -33,4 +33,4 @@ keywords: license: MIT repository-code: https://github.com/fairdataihub/codefair-app date-released: '2024-12-11' -version: 3.2.0 +version: 3.2.1 diff --git a/bot/cwl/index.js b/bot/cwl/index.js index f6ba5574..adc84f28 100644 --- a/bot/cwl/index.js +++ b/bot/cwl/index.js @@ -1,17 +1,17 @@ /** * * This file contains the functions to interact with the CWL files in the repository */ -import { consola } from "consola"; -import { logwatch } from "../utils/logwatch.js"; +import { consola } from 'consola' +import { logwatch } from '../utils/logwatch.js' import { - isRepoPrivate, - createId, - replaceRawGithubUrl, -} from "../utils/tools/index.js"; -import dbInstance from "../db.js"; + isRepoPrivate, + createId, + replaceRawGithubUrl, +} from '../utils/tools/index.js' +import dbInstance from '../db.js' -const CODEFAIR_DOMAIN = process.env.CODEFAIR_APP_DOMAIN; -const { VALIDATOR_URL } = process.env; +const CODEFAIR_DOMAIN = process.env.CODEFAIR_APP_DOMAIN +const { VALIDATOR_URL } = process.env /** * * This function gets the CWL files in the repository @@ -21,74 +21,82 @@ const { VALIDATOR_URL } = process.env; * @returns {Array} - Array of CWL files in the repository */ export function getCWLFiles(context, owner, repository) { - return new Promise((resolve, reject) => { - logwatch.info("Checking for CWL files in the repository..."); - - const cwlFiles = []; - const cwlObject = { - contains_cwl_files: false, - files: [], - removed_files: [], - } + return new Promise((resolve, reject) => { + logwatch.info('Checking for CWL files in the repository...') - const searchDirectory = async function (path) { - try { - const repoContent = await context.octokit.repos.getContent({ - owner, - path, - repo: repository.name, - }); - - for (const file of repoContent.data) { - if (file.type === "file" && file.name.endsWith(".cwl")) { - cwlObject.files.push(file); - } - if (file.type === "dir") { - await searchDirectory(file.path); - } - } - } catch (error) { - if (error.status === 404) { - // Repository is empty - resolve(cwlObject); - return; + const cwlFiles = [] + const cwlObject = { + contains_cwl_files: false, + files: [], + removed_files: [], } - logwatch.error( - { - message: "Error finding CWL files throughout the repository:", - error, - }, - true - ); - reject(error); - } - }; - - // Call the async function and handle its promise - searchDirectory("") - .then(async () => { - try { - // Check if the db entry exists for the repository - const existingCWL = await dbInstance.cwlValidation.findUnique({ - where: { - repository_id: repository.id, + + const searchDirectory = async function (path) { + try { + const repoContent = await context.octokit.repos.getContent({ + owner, + path, + repo: repository.name, + }) + + for (const file of repoContent.data) { + if (file.type === 'file' && file.name.endsWith('.cwl')) { + logwatch.info(`CWL file found: ${file.name}`) + cwlObject.files.push(file) + } + if (file.type === 'dir') { + await searchDirectory(file.path) + } + } + } catch (error) { + if (error.status === 404) { + // Repository is empty + resolve(cwlObject) + return + } + logwatch.error( + { + message: + 'Error finding CWL files throughout the repository:', + error, + }, + true + ) + reject(error) } - }); - - if (existingCWL && existingCWL?.contains_cwl_files) { - cwlObject.contains_cwl_files = existingCWL.contains_cwl_files; - } - - cwlObject.contains_cwl_files = cwlObject.files.length > 0; - - resolve(cwlObject); - } catch (error) { - console.log("Error getting CWL files:", error); - throw new Error("Error getting the CWL files: ", JSON.stringify(error), { cause: error }); } - }) - .catch(reject); - }); + + // Call the async function and handle its promise + searchDirectory('') + .then(async () => { + try { + // Check if the db entry exists for the repository + const existingCWL = + await dbInstance.cwlValidation.findUnique({ + where: { + repository_id: repository.id, + }, + }) + + if (existingCWL && existingCWL?.contains_cwl_files) { + cwlObject.contains_cwl_files = + existingCWL.contains_cwl_files + } + + cwlObject.contains_cwl_files = cwlObject.files.length > 0 + + resolve(cwlObject) + } catch (error) { + console.log('Error getting CWL files:', error) + throw new Error( + 'Error getting the CWL files: ', + JSON.stringify(error), + { cause: error } + ) + } + }) + .catch(reject) + }) } /** @@ -97,33 +105,42 @@ export function getCWLFiles(context, owner, repository) { * @returns {Array} - Array containing the validation status and message */ export async function validateCWLFile(downloadUrl) { - try { - const response = await fetch(`${VALIDATOR_URL}/validate-cwl`, { - body: JSON.stringify({ - file_path: downloadUrl, - }), - headers: { - "Content-Type": "application/json", - }, - method: "POST", - }); - if (!response.ok && response.status === 400) { - const error = await response.json(); - // consola.warn("Validation error:", error.error); - return [false, error.error]; - } - if (!response.ok && response.status === 500) { - logwatch.error({message: "Error validating CWL file:", validation_response: response}, true); - return [false, "Error validating CWL file"]; - } - if (response.ok) { - const data = await response.json(); - return [true, data.output]; + try { + const response = await fetch(`${VALIDATOR_URL}/validate-cwl`, { + body: JSON.stringify({ + file_path: downloadUrl, + }), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) + if (!response.ok && response.status === 400) { + const error = await response.json() + // consola.warn("Validation error:", error.error); + return [false, error.error] + } + if (!response.ok && response.status === 500) { + logwatch.error( + { + message: 'Error validating CWL file:', + validation_response: response, + }, + true + ) + return [false, 'Error validating CWL file'] + } + if (response.ok) { + const data = await response.json() + return [true, data.output] + } + } catch (e) { + logwatch.error( + { message: 'Error validating CWL file:', error: e }, + true + ) + return [false, 'Error validating CWL file'] } - } catch (e) { - logwatch.error({message: "Error validating CWL file:", error: e}, true); - return [false, "Error validating CWL file"]; - } } /** @@ -136,222 +153,229 @@ export async function validateCWLFile(downloadUrl) { * @returns */ export async function applyCWLTemplate( - subjects, - baseTemplate, - repository, - owner, - context, + subjects, + baseTemplate, + repository, + owner, + context ) { - const privateRepo = await isRepoPrivate(context, owner, repository.name); - const identifier = createId(); - const overallSection = `\n\n## Language Specific Standards\n\nTo make your software FAIR is it important to follow language specific standards and best practices. Codefair will check below that your code complies with applicable standards,`; - let url = `${CODEFAIR_DOMAIN}/dashboard/${owner}/${repository.name}/view/cwl-validation`; - - // Delete file entries from db if they were removed from the repository - if (subjects.cwl.removed_files.length > 0) { - // Remove the files from the database - const existingCWL = await dbInstance.cwlValidation.findUnique({ - where: { - repository_id: repository.id, - }, - }); - - if (existingCWL) { - const newFiles = existingCWL.files.filter((file) => { - return !subjects.cwl.removed_files.includes(file.path); - }); - - const newDate = new Date(); - await dbInstance.cwlValidation.update({ - data: { - contains_cwl_files: newFiles.length > 0, - files: newFiles, - updated_at: newDate, - }, - where: { repository_id: repository.id }, - }); - } - } - - // New/Modified CWL files were found, begin validation workflow - const cwlFiles = []; - let validOverall = true; - let tableContent = ""; - let failedCount = 0; - const existingCWL = await dbInstance.cwlValidation.findUnique({ - where: { - repository_id: repository.id, - }, - }); - - if (subjects.cwl.files.length === 0) { - logwatch.warn( - `No CWL files found in the repository, ${repository.name}`, - ); - } - - logwatch.start("Validating CWL files for", repository.name); - // Validate each CWL file from list\ - logwatch.info(`Validating ${JSON.stringify(subjects.cwl)} CWL files`); - if (subjects.cwl.files.length > 0) { - for (const file of subjects.cwl.files) { - const fileSplit = file.name.split("."); - - if (fileSplit.includes("cwl")) { - const downloadUrl = - file?.commitId && !privateRepo - ? file.download_url.replace("/main/", `/${file.commitId}/`) - : file.download_url; // Replace the branch with the commit id if commit id is available and the repo is public - - const [isValidCWL, validationMessage] = - await validateCWLFile(downloadUrl); - - if (!isValidCWL && validOverall) { - // Overall status of CWL validations is invalid - validOverall = false; - } - - if (!isValidCWL) { - failedCount += 1; - } - - const [modifiedValidationMessage, lineNumber1, lineNumber2] = - replaceRawGithubUrl(validationMessage, downloadUrl, file.html_url); - - // Add the line numbers to the URL if they exist - if (lineNumber1) { - file.html_url += `#L${lineNumber1}`; - if (lineNumber2) { - file.html_url += `-L${lineNumber2}`; - } - } - - // Create a new object for the file entry to be added to the db - const newDate = Math.floor(Date.now() / 1000); - cwlFiles.push({ - href: file.html_url, - last_modified: newDate, - last_validated: newDate, - path: file.path, - validation_message: modifiedValidationMessage, - validation_status: isValidCWL ? "valid" : "invalid", - }); - - // Apply the validation file count to the analytics collection on the db - const analyticsCollection = dbInstance.analytics; - await analyticsCollection.upsert({ - create: { - cwl_validated_file_count: 1, // Start count at 1 when creating - id: repository.id, // Create a new record if it doesn't exist - }, - update: { - cwl_validated_file_count: { - increment: 1, + const privateRepo = await isRepoPrivate(context, owner, repository.name) + const identifier = createId() + const overallSection = `\n\n## Language Specific Standards\n\nTo make your software FAIR is it important to follow language specific standards and best practices. Codefair will check below that your code complies with applicable standards,` + let url = `${CODEFAIR_DOMAIN}/dashboard/${owner}/${repository.name}/view/cwl-validation` + + // Delete file entries from db if they were removed from the repository + if (subjects.cwl.removed_files.length > 0) { + // Remove the files from the database + const existingCWL = await dbInstance.cwlValidation.findUnique({ + where: { + repository_id: repository.id, }, - }, - where: { - id: repository.id, - }, - }); - - // Add the file to the table content of the issue dashboard - tableContent += `| ${file.path} | ${isValidCWL ? "✔️" : "❌"} |\n`; - - logwatch.success( - `File: ${file.path} is ${isValidCWL ? "valid" : "invalid"}`, - ); - } + }) + + if (existingCWL) { + const newFiles = existingCWL.files.filter((file) => { + return !subjects.cwl.removed_files.includes(file.path) + }) + + const newDate = new Date() + await dbInstance.cwlValidation.update({ + data: { + contains_cwl_files: newFiles.length > 0, + files: newFiles, + updated_at: newDate, + }, + where: { repository_id: repository.id }, + }) + } } - } - - // Entry does not exist in the db, create a new one (no old files exist, first time seeing cwl files) - if (!existingCWL) { - await dbInstance.cwlValidation.create({ - data: { - contains_cwl_files: subjects.cwl.contains_cwl_files, - files: cwlFiles, - identifier, - overall_status: validOverall ? "valid" : "invalid", - repository: { - connect: { - id: repository.id, - }, + + // New/Modified CWL files were found, begin validation workflow + const cwlFiles = [] + let validOverall = true + let tableContent = '' + let failedCount = 0 + const existingCWL = await dbInstance.cwlValidation.findUnique({ + where: { + repository_id: repository.id, }, - }, - }); - - if (!cwlFiles.length > 0) { - logwatch.warn( - `No CWL files found in the repository, ${repository.name}, skipping CWL section`, - ); - return baseTemplate; + }) + + if (subjects.cwl.files.length === 0) { + logwatch.warn( + `No CWL files found in the repository, ${repository.name}` + ) } - } else { - // An entry exists in the db, thus possible old files exist (merge both lists) - validOverall = true; - const fileMap = new Map(); - - // Add existing files to the map - existingCWL.files.forEach((file) => { - fileMap.set(file.path, file); - }); - // Add new files to the map, replacing any existing entries with the same path - cwlFiles.forEach((file) => { - fileMap.set(file.path, file); - }); - - // Convert the map back to an array - const newFiles = Array.from(fileMap.values()); - - // Check if the overall status is still valid - for (const file of newFiles) { - if (file.validation_status === "invalid") { - validOverall = false; - break; - } + + logwatch.start('Validating CWL files for', repository.name) + // Validate each CWL file from list\ + logwatch.info(`Validating ${JSON.stringify(subjects.cwl)} CWL files`) + if (subjects.cwl.files.length > 0) { + for (const file of subjects.cwl.files) { + const fileSplit = file.name.split('.') + + if (fileSplit.includes('cwl')) { + const downloadUrl = + file?.commitId && !privateRepo + ? file.download_url.replace( + '/main/', + `/${file.commitId}/` + ) + : file.download_url // Replace the branch with the commit id if commit id is available and the repo is public + + const [isValidCWL, validationMessage] = + await validateCWLFile(downloadUrl) + + if (!isValidCWL && validOverall) { + // Overall status of CWL validations is invalid + validOverall = false + } + + if (!isValidCWL) { + failedCount += 1 + } + + const [modifiedValidationMessage, lineNumber1, lineNumber2] = + replaceRawGithubUrl( + validationMessage, + downloadUrl, + file.html_url + ) + + // Add the line numbers to the URL if they exist + if (lineNumber1) { + file.html_url += `#L${lineNumber1}` + if (lineNumber2) { + file.html_url += `-L${lineNumber2}` + } + } + + // Create a new object for the file entry to be added to the db + const newDate = Math.floor(Date.now() / 1000) + cwlFiles.push({ + href: file.html_url, + last_modified: newDate, + last_validated: newDate, + path: file.path, + validation_message: modifiedValidationMessage, + validation_status: isValidCWL ? 'valid' : 'invalid', + }) + + // Apply the validation file count to the analytics collection on the db + const analyticsCollection = dbInstance.analytics + await analyticsCollection.upsert({ + create: { + cwl_validated_file_count: 1, // Start count at 1 when creating + id: repository.id, // Create a new record if it doesn't exist + }, + update: { + cwl_validated_file_count: { + increment: 1, + }, + }, + where: { + id: repository.id, + }, + }) + + // Add the file to the table content of the issue dashboard + tableContent += `| ${file.path} | ${isValidCWL ? '✔️' : '❌'} |\n` + + logwatch.success( + `File: ${file.path} is ${isValidCWL ? 'valid' : 'invalid'}` + ) + } + } } - await dbInstance.cwlValidation.update({ - data: { - contains_cwl_files: newFiles.length > 0, - files: [...newFiles], - overall_status: validOverall ? "valid" : "invalid", - }, - where: { repository_id: repository.id }, - }); - - if (!newFiles.length > 0) { - // All CWL files were removed from the repository - logwatch.warn( - `All CWL files were removed from: ${repository.name}, skipping CWL section` - ); - return baseTemplate; + // Entry does not exist in the db, create a new one (no old files exist, first time seeing cwl files) + if (!existingCWL) { + await dbInstance.cwlValidation.create({ + data: { + contains_cwl_files: subjects.cwl.contains_cwl_files, + files: cwlFiles, + identifier, + overall_status: validOverall ? 'valid' : 'invalid', + repository: { + connect: { + id: repository.id, + }, + }, + }, + }) + + if (!cwlFiles.length > 0) { + logwatch.warn( + `No CWL files found in the repository, ${repository.name}, skipping CWL section` + ) + return baseTemplate + } } else { - // Recreate the table content to include the new and old cwl files - logwatch.start( - "Recreating the table content for the CWL section to include new and old files", - ); - tableContent = ""; - failedCount = 0; - newFiles.forEach((file) => { - if (file.validation_status === "invalid") { - failedCount += 1; + // An entry exists in the db, thus possible old files exist (merge both lists) + validOverall = true + const fileMap = new Map() + + // Add existing files to the map + existingCWL.files.forEach((file) => { + fileMap.set(file.path, file) + }) + // Add new files to the map, replacing any existing entries with the same path + cwlFiles.forEach((file) => { + fileMap.set(file.path, file) + }) + + // Convert the map back to an array + const newFiles = Array.from(fileMap.values()) + + // Check if the overall status is still valid + for (const file of newFiles) { + if (file.validation_status === 'invalid') { + validOverall = false + break + } } - if (file.validation_status === "invalid" && validOverall) { - validOverall = false; + await dbInstance.cwlValidation.update({ + data: { + contains_cwl_files: newFiles.length > 0, + files: [...newFiles], + overall_status: validOverall ? 'valid' : 'invalid', + }, + where: { repository_id: repository.id }, + }) + + if (!newFiles.length > 0) { + // All CWL files were removed from the repository + logwatch.warn( + `All CWL files were removed from: ${repository.name}, skipping CWL section` + ) + return baseTemplate + } else { + // Recreate the table content to include the new and old cwl files + logwatch.start( + 'Recreating the table content for the CWL section to include new and old files' + ) + tableContent = '' + failedCount = 0 + newFiles.forEach((file) => { + if (file.validation_status === 'invalid') { + failedCount += 1 + } + + if (file.validation_status === 'invalid' && validOverall) { + validOverall = false + } + + tableContent += `| ${file.path} | ${file.validation_status === 'valid' ? '✔️' : '❌'} |\n` + }) } - tableContent += `| ${file.path} | ${file.validation_status === "valid" ? "✔️" : "❌"} |\n`; - }); + subjects.cwl.files = newFiles // okay to replace at this stage, used to just get the length of the new and old files for the dashboard } - subjects.cwl.files = newFiles; // okay to replace at this stage, used to just get the length of the new and old files for the dashboard - } - - const cwlBadge = `[![CWL](https://img.shields.io/badge/View_CWL_Report-0ea5e9.svg)](${url})`; - baseTemplate += `${overallSection}\n\n### CWL Validations ${validOverall ? "✔️" : "❗"}\n\nCodefair has detected that you are following the Common Workflow Language (CWL) standard to describe your command line tool. Codefair ran the [cwltool validator](https://cwltool.readthedocs.io/en/latest/) and ${validOverall ? `all ***${subjects.cwl.files.length}*** CWL file(s) in your repository are valid.` : `***${failedCount}/${subjects.cwl.files.length}*** CWL file(s) in your repository are not valid.`}\n\n
\nSummary of the validation report\n\n| File | Validation result |\n| :---- | :----: |\n${tableContent}
\n\nTo view the full report of each CWL file or to rerun the validation, click the "View CWL Report" button below.\n\n${cwlBadge}`; + const cwlBadge = `[![CWL](https://img.shields.io/badge/View_CWL_Report-0ea5e9.svg)](${url})` + baseTemplate += `${overallSection}\n\n### CWL Validations ${validOverall ? '✔️' : '❗'}\n\nCodefair has detected that you are following the Common Workflow Language (CWL) standard to describe your command line tool. Codefair ran the [cwltool validator](https://cwltool.readthedocs.io/en/latest/) and ${validOverall ? `all ***${subjects.cwl.files.length}*** CWL file(s) in your repository are valid.` : `***${failedCount}/${subjects.cwl.files.length}*** CWL file(s) in your repository are not valid.`}\n\n
\nSummary of the validation report\n\n| File | Validation result |\n| :---- | :----: |\n${tableContent}
\n\nTo view the full report of each CWL file or to rerun the validation, click the "View CWL Report" button below.\n\n${cwlBadge}` - logwatch.success("CWL template section applied"); - return baseTemplate; + logwatch.success('CWL template section applied') + return baseTemplate } diff --git a/bot/package.json b/bot/package.json index 02d40ebe..3971df7b 100644 --- a/bot/package.json +++ b/bot/package.json @@ -1,71 +1,71 @@ { - "name": "codefair-bot", - "version": "3.0.0", - "private": true, - "description": "Checks for License and Citation in a Github repo", - "author": "slugb0t", - "license": "MIT", - "homepage": "https://github.com//", - "keywords": [ - "probot", - "github", - "probot-app" - ], - "scripts": { - "dev-old": "probot run ./index.js ", - "dev": "nodemon --watch ./ --exec \"run-p dev:smee dev:start\"", - "start": "node ./main.js", - "dev:smee": "smee -u https://smee.io/n0NQN1LUpx2kI2T1 -p 3001", - "dev:start": "node -r dotenv/config ./main.js", - "build-css": "tailwindcss build -i ./public/assets/css/tailwind.css -o ./public/assets/css/styles.css", - "watch-css": "npx tailwindcss build -i ./public/assets/css/tailwind.css -o ./public/assets/css/styles.css --watch", - "build": "npm run build-css && npm run prisma:generate", - "migrate": "tsx ./migrate.ts", - "dev-migrate": "tsx ./dev-migrate.ts", - "test": "jest", - "format": "prettier --write .", - "prisma:migrate:deploy": "prisma migrate deploy", - "prisma:migrate:dev": "prisma migrate dev --preview-feature", - "prisma:studio": "prisma studio", - "prisma:db:push": "prisma db push", - "prisma:db:pull": "prisma db pull", - "prisma:generate": "prisma generate", - "scripts:truncate:tables": "tsx ./scripts/prismaM.ts" - }, - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "@prisma/client": "^5.19.1", - "axios": "^1.6.8", - "consola": "^3.2.3", - "cwl-ts-auto": "^0.1.3", - "dayjs": "^1.11.11", - "express": "^4.21.0", - "humanparser": "^2.7.0", - "js-yaml": "^4.1.0", - "mongodb": "^6.5.0", - "nanoid": "^5.0.7", - "prisma": "^5.19.1", - "probot": "12.4.0", - "url": "^0.11.3" - }, - "type": "module", - "devDependencies": { - "autoprefixer": "^10.4.19", - "dotenv": "^16.4.5", - "jest": "^29.0.0", - "nodemon": "^3.1.0", - "npm-run-all": "^4.1.5", - "postcss": "^8.4.38", - "prettier": "^3.4.2", - "smee-client": "^2.0.1", - "tailwindcss": "^3.4.3", - "tsx": "^4.16.2", - "typescript": "^5.5.4" - }, - "jest": { - "testEnvironment": "node" - }, - "engines": { - "node": ">=20" - } + "name": "codefair-bot", + "version": "3.2.1", + "private": true, + "description": "Checks for License and Citation in a Github repo", + "author": "slugb0t", + "license": "MIT", + "homepage": "https://github.com//", + "keywords": [ + "probot", + "github", + "probot-app" + ], + "scripts": { + "dev-old": "probot run ./index.js ", + "dev": "nodemon --watch ./ --exec \"run-p dev:smee dev:start\"", + "start": "node ./main.js", + "dev:smee": "smee -u https://smee.io/n0NQN1LUpx2kI2T1 -p 3001", + "dev:start": "node -r dotenv/config ./main.js", + "build-css": "tailwindcss build -i ./public/assets/css/tailwind.css -o ./public/assets/css/styles.css", + "watch-css": "npx tailwindcss build -i ./public/assets/css/tailwind.css -o ./public/assets/css/styles.css --watch", + "build": "npm run build-css && npm run prisma:generate", + "migrate": "tsx ./migrate.ts", + "dev-migrate": "tsx ./dev-migrate.ts", + "test": "jest", + "format": "prettier --write .", + "prisma:migrate:deploy": "prisma migrate deploy", + "prisma:migrate:dev": "prisma migrate dev --preview-feature", + "prisma:studio": "prisma studio", + "prisma:db:push": "prisma db push", + "prisma:db:pull": "prisma db pull", + "prisma:generate": "prisma generate", + "scripts:truncate:tables": "tsx ./scripts/prismaM.ts" + }, + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "@prisma/client": "^5.19.1", + "axios": "^1.6.8", + "consola": "^3.2.3", + "cwl-ts-auto": "^0.1.3", + "dayjs": "^1.11.11", + "express": "^4.21.0", + "humanparser": "^2.7.0", + "js-yaml": "^4.1.0", + "mongodb": "^6.5.0", + "nanoid": "^5.0.7", + "prisma": "^5.19.1", + "probot": "12.4.0", + "url": "^0.11.3" + }, + "type": "module", + "devDependencies": { + "autoprefixer": "^10.4.19", + "dotenv": "^16.4.5", + "jest": "^29.0.0", + "nodemon": "^3.1.0", + "npm-run-all": "^4.1.5", + "postcss": "^8.4.38", + "prettier": "^3.4.2", + "smee-client": "^2.0.1", + "tailwindcss": "^3.4.3", + "tsx": "^4.16.2", + "typescript": "^5.5.4" + }, + "jest": { + "testEnvironment": "node" + }, + "engines": { + "node": ">=20" + } } diff --git a/codemeta.json b/codemeta.json index b2fe42a1..5845980b 100644 --- a/codemeta.json +++ b/codemeta.json @@ -65,7 +65,7 @@ "relatedLink": [ "https://docs.codefair.io/" ], - "schema:releaseNotes": "## v3.1.1 - 11-12-2024\n\n### Fixed\n\n- Remove outdated URL links in the template renderer to ensure the correct and current links are used for metadata, CWL, and license templates.", - "version": "3.2.0", + "schema:releaseNotes": "## v3.2.1 - 12-12-2024\n\n### Added\n- Convert logging from 'consola' to 'logwatch' for improved log management and consistency across the application.\n\n### Fixed\n- Update CI and deployment workflows to include new environment variables 'BOT_LOGWATCH_URL' and 'VALIDATOR_URL'.\n- Patch to Zenodo workflow that was causing the user to be notified of a failed Zenodo upload when the upload was successful.\n- Fix issue with the metadata validation endpoint that was causing the service to return a 405 error.", + "version": "3.2.1", "type": "SoftwareSourceCode" } \ No newline at end of file diff --git a/ui/components.d.ts b/ui/components.d.ts index 01eda1f3..983769d2 100644 --- a/ui/components.d.ts +++ b/ui/components.d.ts @@ -19,6 +19,7 @@ declare module 'vue' { NCollapseItem: typeof import('naive-ui')['NCollapseItem'] NDivider: typeof import('naive-ui')['NDivider'] NDropdown: typeof import('naive-ui')['NDropdown'] + NEmpty: typeof import('naive-ui')['NEmpty'] NFlex: typeof import('naive-ui')['NFlex'] NForm: typeof import('naive-ui')['NForm'] NFormItem: typeof import('naive-ui')['NFormItem'] diff --git a/ui/error.vue b/ui/error.vue index 12ebb86a..1fd3918f 100644 --- a/ui/error.vue +++ b/ui/error.vue @@ -52,8 +52,7 @@ if (props.error) { } catch (error) { console.error("Redirection error:", error); } - } - else { + } else { push.error({ title: "Something went wrong", }); diff --git a/ui/layouts/default.vue b/ui/layouts/default.vue index 94bd1aea..9484f167 100644 --- a/ui/layouts/default.vue +++ b/ui/layouts/default.vue @@ -74,7 +74,7 @@ const toggleMobileMenu = () => { Dashboard @@ -204,7 +204,7 @@ const toggleMobileMenu = () => { v-if="breadcrumbsStore.shouldShowBreadcrumbs" class="pb-3" > - + Dashboard diff --git a/ui/pages/dashboard/[owner]/[repo]/index.vue b/ui/pages/dashboard/[owner]/[repo]/index.vue index 841412b7..ed63548c 100644 --- a/ui/pages/dashboard/[owner]/[repo]/index.vue +++ b/ui/pages/dashboard/[owner]/[repo]/index.vue @@ -81,7 +81,12 @@ if (error.value) { } } -if ((data.value?.codeMetadataRequest?.codemetaStatus === "invalid" || data.value?.codeMetadataRequest?.citationStatus === "invalid") && (data.value?.codeMetadataRequest?.containsCodemeta || data.value?.codeMetadataRequest?.containsCitation)) { +if ( + (data.value?.codeMetadataRequest?.codemetaStatus === "invalid" || + data.value?.codeMetadataRequest?.citationStatus === "invalid") && + (data.value?.codeMetadataRequest?.containsCodemeta || + data.value?.codeMetadataRequest?.containsCitation) +) { displayMetadataValidationResults.value = true; } @@ -136,10 +141,10 @@ const rerunCodefairChecks = async (rerunType: string) => { }); await $fetch(`/api/${owner}/${repo}/rerun`, { - headers: useRequestHeaders(["cookie"]), body: { rerunType, }, + headers: useRequestHeaders(["cookie"]), method: "POST", }) .then(() => { @@ -211,12 +216,18 @@ const handleSettingsSelect = (key: any) => {

FAIR Compliance Dashboard

- + - Settings + Settings
@@ -234,7 +245,10 @@ const handleSettingsSelect = (key: any) => {
- + @@ -254,7 +268,10 @@ const handleSettingsSelect = (key: any) => { Contains a valid license - +