diff --git a/CI/E2E/config.e2e.json b/CI/E2E/config.e2e.json index 78edf034c..68e489458 100644 --- a/CI/E2E/config.e2e.json +++ b/CI/E2E/config.e2e.json @@ -7,7 +7,7 @@ "editDatasetSampleEnabled": true, "editMetadataEnabled": true, "editPublishedData": false, - "editSampleEnabled": true, + "addSampleEnabled": true, "externalAuthEndpoint": "/api/v3/auth/msad", "facility": "ESS", "loginFacilityLabel": "ESS", @@ -119,4 +119,4 @@ "shoppingCartEnabled": true, "shoppingCartOnHeader": true, "tableSciDataEnabled": true -} \ No newline at end of file +} diff --git a/CI/E2E/frontend.config.json b/CI/E2E/frontend.config.json index 36dab2143..187a3bfe5 100644 --- a/CI/E2E/frontend.config.json +++ b/CI/E2E/frontend.config.json @@ -6,7 +6,7 @@ "editDatasetSampleEnabled": true, "editMetadataEnabled": true, "editPublishedData": false, - "editSampleEnabled": true, + "addSampleEnabled": true, "externalAuthEndpoint": "/api/v3/auth/msad", "facility": "ESS", "loginFacilityLabel": "ESS", @@ -36,7 +36,7 @@ "order": 1, "type": "standard", "enabled": true - }, + }, { "name": "datasetName", "order": 2, diff --git a/CI/MLZ/frontend.config.json b/CI/MLZ/frontend.config.json index e28d3f63b..2792d8c8a 100644 --- a/CI/MLZ/frontend.config.json +++ b/CI/MLZ/frontend.config.json @@ -12,7 +12,7 @@ ], "externalAuthEndpoint": "/auth/ldap", "editMetadataEnabled": true, - "editSampleEnabled": true, + "addSampleEnabled": true, "editPublishedData": true, "scienceSearchEnabled": true, "facility": "MLZ", @@ -118,7 +118,5 @@ "scienceSearchUnitsEnabled": false, "metadataStructure": "", "loginFormEnabled": true, - "oAuth2Endpoints": [ - {"displayText": "MLZ", "authURL":"api/v3/auth/oidc"} - ] + "oAuth2Endpoints": [{ "displayText": "MLZ", "authURL": "api/v3/auth/oidc" }] } diff --git a/CI/MLZ/frontend.config.ts b/CI/MLZ/frontend.config.ts index 7470fc05e..6287404c6 100644 --- a/CI/MLZ/frontend.config.ts +++ b/CI/MLZ/frontend.config.ts @@ -4,17 +4,17 @@ export default { retrieveDestinations: [ { option: "MLZ", - location: "/home/out" + location: "/home/out", }, { - option: "MLZ S3 (Testphase)" - } + option: "MLZ S3 (Testphase)", + }, ], accessTokenPrefix: "Bearer ", lbTokenPrefix: "Bearer ", externalAuthEndpoint: "", editMetadataEnabled: true, - editSampleEnabled: true, + addSampleEnabled: true, editPublishedData: true, scienceSearchEnabled: true, facility: "MLZ", @@ -33,74 +33,74 @@ export default { name: "select", order: 0, type: "standard", - enabled: true + enabled: true, }, { name: "datasetName", order: 1, type: "standard", - enabled: true + enabled: true, }, { name: "runNumber", order: 2, type: "standard", - enabled: true + enabled: true, }, { name: "sourceFolder", order: 3, type: "standard", - enabled: true + enabled: true, }, { name: "size", order: 4, type: "standard", - enabled: true + enabled: true, }, { name: "creationTime", order: 5, type: "standard", - enabled: true + enabled: true, }, { name: "type", order: 6, type: "standard", - enabled: true + enabled: true, }, { name: "image", order: 7, type: "standard", - enabled: true + enabled: true, }, { name: "metadata", order: 8, type: "standard", - enabled: true + enabled: true, }, { name: "proposalId", order: 9, type: "standard", - enabled: true + enabled: true, }, { name: "ownerGroup", order: 10, type: "standard", - enabled: true + enabled: true, }, { name: "dataStatus", order: 11, type: "standard", - enabled: true - } + enabled: true, + }, ], logbookEnabled: true, metadataPreviewEnabled: true, @@ -121,8 +121,9 @@ export default { metadataStructure: "", loginFormEnabled: true, oAuth2Endpoints: [ - { - displayText: "MLZ", - authURL: "api/v3/auth/oidc"} - ] + { + displayText: "MLZ", + authURL: "api/v3/auth/oidc", + }, + ], }; diff --git a/package-lock.json b/package-lock.json index 029d94b31..cda024174 100644 --- a/package-lock.json +++ b/package-lock.json @@ -879,9 +879,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -3029,9 +3029,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", + "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", "dev": true }, "node_modules/@types/luxon": { @@ -3059,9 +3059,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", "dependencies": { "undici-types": "~5.26.4" } @@ -3736,16 +3736,16 @@ "dev": true }, "node_modules/@user-office-software/duo-logger": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@user-office-software/duo-logger/-/duo-logger-2.1.1.tgz", - "integrity": "sha512-ud/uaYLkOANmtpUO4qLEMYHwIJlcwLGtsjBz5cfw/90/9aDSgOEkbUofjkLcqi+DgngI2n/XxyUCF0m47SGUdw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@user-office-software/duo-logger/-/duo-logger-2.2.1.tgz", + "integrity": "sha512-i7CXE38HGm2a029VA8pqq6EJc0zd5xpUyIMQHHd5cY4jJbWTrIyKqiZwN3dyVollvA9FQwKzM/AN71x6zg7M3Q==", "dependencies": { "fast-safe-stringify": "^2.1.1", - "gelf-pro": "^1.3.8" + "gelf-pro": "^1.3.11" }, "engines": { - "node": ">=16.14.0", - "npm": ">=8.5.0" + "node": ">=18.0.0", + "npm": ">=9.0.0" } }, "node_modules/@user-office-software/duo-message-broker": { @@ -7243,9 +7243,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", - "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -7469,9 +7469,9 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/gelf-pro": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/gelf-pro/-/gelf-pro-1.3.10.tgz", - "integrity": "sha512-s6EnNLn+YTtwQAwe3dt0ZdBsmrfJTa21XQ8PnKfkIzSKjWUjq14yqm+bMFFANfYoPAuV/zkMaPdXYjC/PvRbcg==", + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/gelf-pro/-/gelf-pro-1.3.11.tgz", + "integrity": "sha512-y6DOxU40U4Sd+ECSLtMMtOTig1gEmnIHnvfocyvH+okc6LSfd7ADirT1tbzpAMzOXIgDG/2j9psZ1ck3HXfjEA==", "dependencies": { "lodash": "~4.17.21" } @@ -9225,9 +9225,9 @@ } }, "node_modules/jose": { - "version": "4.15.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", - "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz", + "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -9912,11 +9912,11 @@ } }, "node_modules/mathjs": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-12.4.0.tgz", - "integrity": "sha512-4Moy0RNjwMSajEkGGxNUyMMC/CZAcl87WBopvNsJWB4E4EFebpTedr+0/rhqmnOSTH3Wu/3WfiWiw6mqiaHxVw==", + "version": "12.4.1", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-12.4.1.tgz", + "integrity": "sha512-welnW3khgwYjPYvECFHO+xkCxAx9IKIIPDDWPi8B5rKAvmgoEHnQX9slEmHKZTNaJiE+OS4qrJJcB4sfDn/4sw==", "dependencies": { - "@babel/runtime": "^7.23.9", + "@babel/runtime": "^7.24.0", "complex.js": "^2.1.1", "decimal.js": "^10.4.3", "escape-latex": "^1.2.0", @@ -10800,9 +10800,9 @@ } }, "node_modules/mongoose": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.0.tgz", - "integrity": "sha512-la93n6zCYRbPS+c5N9oTDAktvREy5OT9OCljp1Tah0y3+p8UPMTAoabWaLZMdzYruOtF9/9GRf6MasaZjiZP1A==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.2.tgz", + "integrity": "sha512-6sMxe1d3k/dBjiOX4ExNTNOP0g1x0iq8eXyg+ttgIXM3HLnQ0IUyXRwVVAPFFY6O4/8uYN5dB0Ec72FrexbPpw==", "dependencies": { "bson": "^6.2.0", "kareem": "2.5.1", @@ -11021,9 +11021,9 @@ "dev": true }, "node_modules/nodemailer": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.11.tgz", - "integrity": "sha512-UiAkgiERuG94kl/3bKfE8o10epvDnl0vokNEtZDPTq9BWzIl6EFT9336SbIT4oaTBD8NmmUTLsQyXHV82eXSWg==", + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", + "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==", "engines": { "node": ">=6.0.0" } @@ -11180,11 +11180,11 @@ } }, "node_modules/openid-client": { - "version": "5.6.4", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.4.tgz", - "integrity": "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==", + "version": "5.6.5", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz", + "integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==", "dependencies": { - "jose": "^4.15.4", + "jose": "^4.15.5", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" diff --git a/src/attachments/attachments.service.spec.ts b/src/attachments/attachments.service.spec.ts index 817fabd63..7cb7ed094 100644 --- a/src/attachments/attachments.service.spec.ts +++ b/src/attachments/attachments.service.spec.ts @@ -24,6 +24,7 @@ const mockAttachment: Attachment = { describe("AttachmentsService", () => { let service: AttachmentsService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let attachmentModel: Model; beforeEach(async () => { diff --git a/src/auth/access-group-provider/access-group-from-multiple-providers.service.ts b/src/auth/access-group-provider/access-group-from-multiple-providers.service.ts index 64936eda5..1107f2b72 100644 --- a/src/auth/access-group-provider/access-group-from-multiple-providers.service.ts +++ b/src/auth/access-group-provider/access-group-from-multiple-providers.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from "@nestjs/common"; +import { Injectable } from "@nestjs/common"; import { UserPayload } from "../interfaces/userPayload.interface"; import { AccessGroupService } from "./access-group.service"; @@ -11,10 +11,7 @@ export class AccessGroupFromMultipleProvidersService extends AccessGroupService super(); } - async getAccessGroups( - //idpPayload: Record, - userPayload: UserPayload, - ): Promise { + async getAccessGroups(userPayload: UserPayload): Promise { const accessGroups: string[] = []; for (const accessGroupProvider of this.accessGroupProviders) { diff --git a/src/auth/access-group-provider/access-group-from-static-values.service.ts b/src/auth/access-group-provider/access-group-from-static-values.service.ts index 01b6a8091..e99c00bc4 100644 --- a/src/auth/access-group-provider/access-group-from-static-values.service.ts +++ b/src/auth/access-group-provider/access-group-from-static-values.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from "@nestjs/common"; +import { Injectable } from "@nestjs/common"; import { AccessGroupService } from "./access-group.service"; /** @@ -11,10 +11,6 @@ export class AccessGroupFromStaticValuesService extends AccessGroupService { } async getAccessGroups(): Promise { - // Logger.log( - // "Static access group getAccessGroups : " + - // this.staticAccessGroups.join(","), - // ); return this.staticAccessGroups; } } diff --git a/src/auth/auth.controller.spec.ts b/src/auth/auth.controller.spec.ts index 619bf72e8..894b3257d 100644 --- a/src/auth/auth.controller.spec.ts +++ b/src/auth/auth.controller.spec.ts @@ -4,15 +4,15 @@ import { AuthService } from "./auth.service"; import { ConfigService } from "@nestjs/config"; class AuthServiceMock { - login(req: Record) { + login() { return { username: "Test User", email: "testUser@gmail.com" }; } - adLogin(req: Record) { + adLogin() { return { username: "Test User", email: "testUser@gmail.com" }; } - whoami(req: Record) { + whoami() { return { username: "Test User", email: "testUser@gmail.com" }; } } diff --git a/src/auth/guards/jwt-auth.guard.ts b/src/auth/guards/jwt-auth.guard.ts index 3e5ec6b73..f6ea04813 100644 --- a/src/auth/guards/jwt-auth.guard.ts +++ b/src/auth/guards/jwt-auth.guard.ts @@ -1,8 +1,4 @@ -import { - ExecutionContext, - Injectable, - UnauthorizedException, -} from "@nestjs/common"; +import { ExecutionContext, Injectable } from "@nestjs/common"; import { Reflector } from "@nestjs/core"; import { AuthGuard } from "@nestjs/passport"; @@ -26,8 +22,6 @@ export class JwtAuthGuard extends AuthGuard("jwt") { err: unknown, // eslint-disable-next-line @typescript-eslint/no-explicit-any user: any, - info: unknown, - context: ExecutionContext, ) { // const allowAny = this.reflector.get( // "allow-any", diff --git a/src/auth/strategies/ldap.strategy.ts b/src/auth/strategies/ldap.strategy.ts index 16af253b8..f77721041 100644 --- a/src/auth/strategies/ldap.strategy.ts +++ b/src/auth/strategies/ldap.strategy.ts @@ -87,7 +87,7 @@ export class LdapStrategy extends PassportStrategy(Strategy, "ldap") { const foundUser = await this.usersService.findOne(userFilter); const jsonUser = JSON.parse(JSON.stringify(foundUser)); - const { password, ...user } = jsonUser; + const { ...user } = jsonUser; user.userId = user._id; // update user identity if needed diff --git a/src/auth/strategies/oidc.strategy.ts b/src/auth/strategies/oidc.strategy.ts index 92a25fb45..974e36b8a 100644 --- a/src/auth/strategies/oidc.strategy.ts +++ b/src/auth/strategies/oidc.strategy.ts @@ -142,7 +142,7 @@ export class OidcStrategy extends PassportStrategy(Strategy, "oidc") { } const jsonUser = JSON.parse(JSON.stringify(user)); - const { password, ...returnUser } = jsonUser; + const { ...returnUser } = jsonUser; returnUser.userId = returnUser._id; return returnUser; diff --git a/src/common/pipes/filter.pipe.ts b/src/common/pipes/filter.pipe.ts index 905c12d0c..55091ffba 100644 --- a/src/common/pipes/filter.pipe.ts +++ b/src/common/pipes/filter.pipe.ts @@ -1,4 +1,4 @@ -import { PipeTransform, Injectable, ArgumentMetadata } from "@nestjs/common"; +import { PipeTransform, Injectable } from "@nestjs/common"; @Injectable() export class FilterPipe @@ -8,10 +8,10 @@ export class FilterPipe { filter?: string; fields?: string } > { - transform( - inValue: { filter?: string; fields?: string }, - metadata: ArgumentMetadata, - ): { filter?: string; fields?: string } { + transform(inValue: { filter?: string; fields?: string }): { + filter?: string; + fields?: string; + } { /* * intercept filter and make sure to convert loopback operators to mongo operators */ diff --git a/src/common/utils.ts b/src/common/utils.ts index 3ae65ddb5..ea18aafdd 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -87,15 +87,28 @@ export const convertToRequestedUnit = ( }; export const mapScientificQuery = ( + key: string, scientific: IScientificFilter[] = [], ): Record => { const scientificFilterQuery: Record = {}; + const keyToFieldMapping: Record = { + scientific: "scientificMetadata", + characteristics: "sampleCharacteristics", + }; + + const field = keyToFieldMapping[key]; + scientific.forEach((scientificFilter) => { const { lhs, relation, rhs, unit } = scientificFilter; - const matchKeyGeneric = `scientificMetadata.${lhs}`; - const matchKeyMeasurement = `scientificMetadata.${lhs}.valueSI`; - const matchUnit = `scientificMetadata.${lhs}.unitSI`; + const formattedLhs = lhs + .trim() + .replace(/[.]/g, "\\.") + .replace(/ /g, "_") + .toLowerCase(); + const matchKeyGeneric = `${field}.${formattedLhs}`; + const matchKeyMeasurement = `${field}.${formattedLhs}.valueSI`; + const matchUnit = `${field}.${formattedLhs}.unitSI`; switch (relation) { case ScientificRelation.EQUAL_TO_STRING: { @@ -134,7 +147,6 @@ export const mapScientificQuery = ( } } }); - return scientificFilterQuery; }; @@ -448,10 +460,10 @@ export const createFullqueryFilter = ( key, fields[key], ) as typeof filterQuery.$text; - } else if (key === "scientific" || key === "sampleCharacteristics") { + } else if (key === "scientific" || key === "characteristics") { filterQuery = { ...filterQuery, - ...mapScientificQuery(fields[key]), + ...mapScientificQuery(key, fields[key]), }; } else if (key === "userGroups") { filterQuery["$or"]?.push({ @@ -587,6 +599,7 @@ const pipelineHandler = { ) => { const match = { $match: mapScientificQuery( + key, fields[key as keyof Y] as unknown as IScientificFilter[], ), }; diff --git a/src/config/configuration.ts b/src/config/configuration.ts index c3fe42a32..e73d42a72 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -1,4 +1,3 @@ -import { Logger } from "@nestjs/common"; import * as fs from "fs"; import { merge } from "lodash"; import localconfiguration from "./localconfiguration"; @@ -58,20 +57,6 @@ const configuration = () => { } }); - // Logger.log("Config SETUP"); - // Logger.log("- Access groups statisc values : " + accessGroupsStaticValues); - // Logger.log("- Admin groups : " + adminGroups); - // Logger.log("- Delete groups : " + deleteGroups ); - // Logger.log("- Create dataset groups : " + createDatasetGroups); - // Logger.log( - // "- Create dataset with pid groups : " + createDatasetWithPidGroups, - // ); - // Logger.log( - // "- Create dataset privileged groups : " + createDatasetPrivilegedGroups, - // ); - // Logger.log("- Create job groups : " + createJobGroups); - // Logger.log("- Update job groups : " + updateJobGroups); - const config = { loggerConfigs: jsonConfigMap.loggers || [defaultLogger], adminGroups: adminGroups.split(",").map((v) => v.trim()) ?? [], diff --git a/src/config/frontend.config.json b/src/config/frontend.config.json index c9569728d..8c5833abf 100644 --- a/src/config/frontend.config.json +++ b/src/config/frontend.config.json @@ -1,13 +1,13 @@ { "accessTokenPrefix": "Bearer ", - "addDatasetEnabled": true, + "addDatasetEnabled": false, "archiveWorkflowEnabled": false, "datasetJsonScientificMetadata": true, "datasetReduceEnabled": true, "editDatasetSampleEnabled": true, "editMetadataEnabled": true, "editPublishedData": false, - "editSampleEnabled": true, + "addSampleEnabled": false, "externalAuthEndpoint": "/api/v3/auth/msad", "facility": "ESS", "loginFacilityLabel": "ESS", @@ -135,4 +135,4 @@ "datasetDetailsShowMissingProposalId": false, "notificationInterceptorEnabled": true, "metadataEditingUnitListDisabled": true -} \ No newline at end of file +} diff --git a/src/datablocks/datablocks.service.spec.ts b/src/datablocks/datablocks.service.spec.ts index 94a4c4031..c8881f80f 100644 --- a/src/datablocks/datablocks.service.spec.ts +++ b/src/datablocks/datablocks.service.spec.ts @@ -35,6 +35,7 @@ const mockDatablock: Datablock = { describe("DatablocksService", () => { let service: DatablocksService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/datasets/datasets.controller.ts b/src/datasets/datasets.controller.ts index eba085099..6655d3b5f 100644 --- a/src/datasets/datasets.controller.ts +++ b/src/datasets/datasets.controller.ts @@ -21,7 +21,6 @@ import { InternalServerErrorException, ConflictException, BadRequestException, - Header, } from "@nestjs/common"; import { MongoError } from "mongodb"; import { @@ -97,7 +96,6 @@ import { TechniqueClass } from "./schemas/technique.schema"; import { RelationshipClass } from "./schemas/relationship.schema"; import { JWTUser } from "src/auth/interfaces/jwt-user.interface"; import { LogbooksService } from "src/logbooks/logbooks.service"; -import { Logbook } from "src/logbooks/schemas/logbook.schema"; import configuration from "src/config/configuration"; import { DatasetType } from "./dataset-type.enum"; @@ -548,10 +546,7 @@ export class DatasetsController { @Req() request: Request, @Body() createDatasetDto: CreateRawDatasetDto | CreateDerivedDatasetDto, ): Promise<{ valid: boolean }> { - const datasetDTO = await this.checkPermissionsForDatasetCreate( - request, - createDatasetDto, - ); + await this.checkPermissionsForDatasetCreate(request, createDatasetDto); const dtoTestRawCorrect = plainToInstance( CreateRawDatasetDto, @@ -923,8 +918,6 @@ export class DatasetsController { @Headers() headers: Record, @Query(new FilterPipe()) queryFilter: { filter?: string }, ): Promise { - const user: JWTUser = request.user as JWTUser; - const mergedFilters = replaceLikeOperator( this.updateMergedFiltersForList( request, @@ -1343,7 +1336,7 @@ export class DatasetsController { @Req() request: Request, @Param("pid") pid: string, ): Promise> { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetRead, @@ -1439,7 +1432,7 @@ export class DatasetsController { @Req() request: Request, @Param("pid") pid: string, ): Promise { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetAttachmentRead, @@ -1483,7 +1476,7 @@ export class DatasetsController { @Param("aid") aid: string, @Body() updateAttachmentDto: UpdateAttachmentDto, ): Promise { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetAttachmentUpdate, @@ -1527,7 +1520,7 @@ export class DatasetsController { @Param("pid") pid: string, @Param("aid") aid: string, ): Promise { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetAttachmentDelete, @@ -1637,7 +1630,7 @@ export class DatasetsController { @Param("pid") pid: string, @Body() createOrigDatablock: unknown, ): Promise<{ valid: boolean; errors: ValidationError[] }> { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetOrigdatablockCreate, @@ -1682,7 +1675,7 @@ export class DatasetsController { @Req() request: Request, @Param("pid") pid: string, ): Promise { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetOrigdatablockRead, @@ -1913,7 +1906,7 @@ export class DatasetsController { @Req() request: Request, @Param("pid") pid: string, ): Promise { - const dataset = await this.checkPermissionsForDatasetExtended( + await this.checkPermissionsForDatasetExtended( request, pid, Action.DatasetDatablockRead, diff --git a/src/datasets/datasets.service.spec.ts b/src/datasets/datasets.service.spec.ts index 9b62fcdc2..9cea8700e 100644 --- a/src/datasets/datasets.service.spec.ts +++ b/src/datasets/datasets.service.spec.ts @@ -87,6 +87,7 @@ const mockDataset: DatasetClass = { describe("DatasetsService", () => { let service: DatasetsService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/elastic-search/elastic-search.service.ts b/src/elastic-search/elastic-search.service.ts index 992004c9c..59d9b4244 100644 --- a/src/elastic-search/elastic-search.service.ts +++ b/src/elastic-search/elastic-search.service.ts @@ -9,7 +9,6 @@ import { import { Client } from "@elastic/elasticsearch"; import { SearchQueryService } from "./providers/query-builder.service"; import { - SearchTotalHits, SearchRequest, AggregationsAggregate, SortOrder, diff --git a/src/elastic-search/providers/query-builder.service.ts b/src/elastic-search/providers/query-builder.service.ts index 911585879..333d09a7d 100644 --- a/src/elastic-search/providers/query-builder.service.ts +++ b/src/elastic-search/providers/query-builder.service.ts @@ -112,6 +112,7 @@ export class SearchQueryService { switch (fieldName) { case FilterFields.ScientificMetadata: const scientificFilterQuery = mapScientificQuery( + fieldName, values as IScientificFilter[], ); diff --git a/src/initial-datasets/initial-datasets.service.spec.ts b/src/initial-datasets/initial-datasets.service.spec.ts index e016e50dc..9067f4239 100644 --- a/src/initial-datasets/initial-datasets.service.spec.ts +++ b/src/initial-datasets/initial-datasets.service.spec.ts @@ -10,6 +10,7 @@ const mockInitialDataset: InitialDataset = { describe("InitialDatasetsService", () => { let service: InitialDatasetsService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let initialDatasetModel: Model; beforeEach(async () => { diff --git a/src/instruments/instruments.service.spec.ts b/src/instruments/instruments.service.spec.ts index 68793cefe..d25cac17a 100644 --- a/src/instruments/instruments.service.spec.ts +++ b/src/instruments/instruments.service.spec.ts @@ -14,6 +14,7 @@ const mockInstrument: Instrument = { describe("InstrumentsService", () => { let service: InstrumentsService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/jobs/jobs.service.spec.ts b/src/jobs/jobs.service.spec.ts index 63be7f982..e9bc33034 100644 --- a/src/jobs/jobs.service.spec.ts +++ b/src/jobs/jobs.service.spec.ts @@ -30,6 +30,7 @@ class PoliciesServiceMock {} describe("JobsService", () => { let service: JobsService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/origdatablocks/origdatablocks.service.spec.ts b/src/origdatablocks/origdatablocks.service.spec.ts index 31268303a..7d5ca7999 100644 --- a/src/origdatablocks/origdatablocks.service.spec.ts +++ b/src/origdatablocks/origdatablocks.service.spec.ts @@ -32,6 +32,7 @@ const mockOrigDatablock: OrigDatablock = { describe("OrigdatablocksService", () => { let service: OrigDatablocksService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/policies/policies.service.spec.ts b/src/policies/policies.service.spec.ts index 075c0aadc..9ca0c1877 100644 --- a/src/policies/policies.service.spec.ts +++ b/src/policies/policies.service.spec.ts @@ -33,6 +33,7 @@ const mockPolicy: Policy = { describe("PoliciesService", () => { let service: PoliciesService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let policyModel: Model; beforeEach(async () => { diff --git a/src/proposals/proposals.service.spec.ts b/src/proposals/proposals.service.spec.ts index f53af3ad9..736369e04 100644 --- a/src/proposals/proposals.service.spec.ts +++ b/src/proposals/proposals.service.spec.ts @@ -30,6 +30,7 @@ const mockProposal: ProposalClass = { describe("ProposalsService", () => { let service: ProposalsService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/proposals/schemas/proposal.schema.ts b/src/proposals/schemas/proposal.schema.ts index b4d3a4ce6..8ebbe4baa 100644 --- a/src/proposals/schemas/proposal.schema.ts +++ b/src/proposals/schemas/proposal.schema.ts @@ -1,10 +1,7 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose"; -import { ApiProperty, getSchemaPath } from "@nestjs/swagger"; +import { ApiProperty } from "@nestjs/swagger"; import { Document } from "mongoose"; -import { - Attachment, - AttachmentSchema, -} from "src/attachments/schemas/attachment.schema"; + import { OwnableClass } from "src/common/schemas/ownable.schema"; import { MeasurementPeriodClass, diff --git a/src/published-data/published-data.controller.ts b/src/published-data/published-data.controller.ts index ec8f03dd5..364c03c29 100644 --- a/src/published-data/published-data.controller.ts +++ b/src/published-data/published-data.controller.ts @@ -439,8 +439,7 @@ export class PublishedDataController { @Param("id") id: string, @Body() data: UpdatePublishedDataDto, ): Promise { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { doi, ...publishedData } = data; + const { ...publishedData } = data; const OAIServerUri = this.configService.get("oaiProviderRoute"); diff --git a/src/published-data/published-data.service.spec.ts b/src/published-data/published-data.service.spec.ts index 1774daff4..12a9937ec 100644 --- a/src/published-data/published-data.service.spec.ts +++ b/src/published-data/published-data.service.spec.ts @@ -32,6 +32,7 @@ const mockPublishedData: PublishedData = { describe("PublishedDataService", () => { let service: PublishedDataService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let model: Model; beforeEach(async () => { diff --git a/src/published-data/published-data.service.ts b/src/published-data/published-data.service.ts index 77fc76f56..97cbed44b 100644 --- a/src/published-data/published-data.service.ts +++ b/src/published-data/published-data.service.ts @@ -2,7 +2,7 @@ import { Inject, Injectable, Scope } from "@nestjs/common"; import { REQUEST } from "@nestjs/core"; import { Request } from "express"; import { InjectModel } from "@nestjs/mongoose"; -import { FilterQuery, Model, QueryOptions } from "mongoose"; +import { FilterQuery, Model } from "mongoose"; import { parseLimitFilters, addCreatedByFields, diff --git a/src/samples/samples.controller.ts b/src/samples/samples.controller.ts index 7aaf51e1b..4f49a3dac 100644 --- a/src/samples/samples.controller.ts +++ b/src/samples/samples.controller.ts @@ -63,7 +63,6 @@ import { Request } from "express"; import { JWTUser } from "src/auth/interfaces/jwt-user.interface"; import { IDatasetFields } from "src/datasets/interfaces/dataset-filters.interface"; import { CreateSubAttachmentDto } from "src/attachments/dto/create-sub-attachment.dto"; -import { User } from "src/users/schemas/user.schema"; @ApiBearerAuth() @ApiTags("samples") diff --git a/src/samples/samples.service.spec.ts b/src/samples/samples.service.spec.ts index b3dffd4d6..9425bec06 100644 --- a/src/samples/samples.service.spec.ts +++ b/src/samples/samples.service.spec.ts @@ -4,6 +4,26 @@ import { Test, TestingModule } from "@nestjs/testing"; import { Model } from "mongoose"; import { SamplesService } from "./samples.service"; import { SampleClass } from "./schemas/sample.schema"; +import { ScientificRelation } from "src/common/scientific-relation.enum"; +import * as utils from "../common/utils"; + +jest.mock("../common/utils", () => { + const mockUtils = jest.requireActual("../common/utils"); + const mockMapScientificQuery = jest.fn(mockUtils.mapScientificQuery); + const mockCreateFullqueryFilter = jest.fn((model, idField, fields) => { + mockMapScientificQuery("characteristics", fields.characteristics); + + // Call the actual createFullqueryFilter for real logic + return mockUtils.createFullqueryFilter(model, idField, fields); + }); + return { + mapScientificQuery: mockMapScientificQuery, + createFullqueryFilter: mockCreateFullqueryFilter, + parseLimitFilters: jest + .fn() + .mockReturnValue({ limit: 10, skip: 0, sort: {} }), + }; +}); const mockSample: SampleClass = { _id: "testId", @@ -23,6 +43,7 @@ const mockSample: SampleClass = { describe("SamplesService", () => { let service: SamplesService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let sampleModel: Model; beforeEach(async () => { @@ -35,7 +56,7 @@ describe("SamplesService", () => { useValue: { new: jest.fn().mockResolvedValue(mockSample), constructor: jest.fn().mockResolvedValue(mockSample), - find: jest.fn(), + find: jest.fn().mockReturnThis(), // Chainable create: jest.fn(), exec: jest.fn(), }, @@ -50,4 +71,46 @@ describe("SamplesService", () => { it("should be defined", () => { expect(service).toBeDefined(); }); + + it("should correctly apply text and characteristics filters to find samples", async () => { + const filter = { + fields: { + text: "test", + characteristics: [ + { + lhs: "test", + relation: ScientificRelation.EQUAL_TO_STRING, + rhs: "test", + unit: "test", + }, + ], + }, + }; + + const mapScientificQueryResponse = { + $text: { + $search: "test", + }, + "sampleCharacteristics.test.value": { + $eq: "test", + }, + }; + + await service.fullquery(filter); + + expect(utils.createFullqueryFilter).toHaveBeenCalledWith( + sampleModel, + "sampleId", + filter.fields, + ); + expect(utils.mapScientificQuery).toHaveBeenCalledWith( + "characteristics", + filter["fields"]["characteristics"], + ); + expect(sampleModel.find).toHaveBeenCalledWith( + mapScientificQueryResponse, + null, + { limit: 10, skip: 0, sort: {} }, + ); + }); }); diff --git a/src/samples/samples.service.ts b/src/samples/samples.service.ts index f57326c98..fd1189102 100644 --- a/src/samples/samples.service.ts +++ b/src/samples/samples.service.ts @@ -31,6 +31,7 @@ export class SamplesService { const createdSample = new this.sampleModel( addCreatedByFields(createSampleDto, username), ); + return createdSample.save(); } diff --git a/src/users/roles.service.spec.ts b/src/users/roles.service.spec.ts index 082fa028a..75d00063b 100644 --- a/src/users/roles.service.spec.ts +++ b/src/users/roles.service.spec.ts @@ -19,7 +19,9 @@ const mockUserRole: UserRole = { describe("RolesService", () => { let service: RolesService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let roleModel: Model; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let userRoleModel: Model; beforeEach(async () => { diff --git a/src/users/user-identities.service.spec.ts b/src/users/user-identities.service.spec.ts index 597638d8c..848d25ef1 100644 --- a/src/users/user-identities.service.spec.ts +++ b/src/users/user-identities.service.spec.ts @@ -25,6 +25,7 @@ const mockUserIdentity: UserIdentity = { describe("UserIdentitiesService", () => { let service: UserIdentitiesService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let userIdentityModel: Model; beforeEach(async () => { diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts index 838063b9b..24a65a86d 100644 --- a/src/users/users.service.spec.ts +++ b/src/users/users.service.spec.ts @@ -62,8 +62,11 @@ const mockUserSettings: UserSettings = { describe("UsersService", () => { let service: UsersService; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let userModel: Model; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let userIdentityModel: Model; + // eslint-disable-next-line @typescript-eslint/no-unused-vars let userSettingsModel: Model; beforeEach(async () => { diff --git a/src/users/users.service.ts b/src/users/users.service.ts index c9c4ec08f..7369730d7 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger, OnModuleInit } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { InjectModel } from "@nestjs/mongoose"; import { genSalt, hash } from "bcrypt"; -import { FilterQuery, Model, ObjectId } from "mongoose"; +import { FilterQuery, Model } from "mongoose"; import { CreateUserIdentityDto } from "./dto/create-user-identity.dto"; import { CreateUserDto } from "./dto/create-user.dto"; import { RolesService } from "./roles.service"; @@ -160,7 +160,7 @@ export class UsersService implements OnModuleInit { ); if (createUserDto.authStrategy !== "local") { - const { password, ...sanitizedCreateUserDto } = createUserDto; + const { ...sanitizedCreateUserDto } = createUserDto; const createdUser = new this.userModel(sanitizedCreateUserDto); return createdUser.save(); } else if (createUserDto.password) { diff --git a/test/Sample.js b/test/Sample.js index 426d6f6d8..915871cd5 100644 --- a/test/Sample.js +++ b/test/Sample.js @@ -11,6 +11,9 @@ let accessTokenAdminIngestor = null, datasetId = null; describe("2200: Sample: Simple Sample", () => { + before(() => { + db.collection("Sample").deleteMany({}); + }); beforeEach((done) => { utils.getToken( appUrl, @@ -63,11 +66,31 @@ describe("2200: Sample: Simple Sample", () => { }); }); + it("0025: should fetch this new sample by characteristics filter", async () => { + const fields = { + characteristics: [ + { lhs: "chemical_formula", relation: "EQUAL_TO_STRING", rhs: "H2O" }, + ], + }; + return request(appUrl) + .get( + `/api/v3/Samples/fullquery?fields=${encodeURIComponent(JSON.stringify(fields))}&limits={}`, + ) + .set("Accept", "application/json") + .set({ Authorization: `Bearer ${accessTokenAdminIngestor}` }) + .expect(TestData.SuccessfulGetStatusCode) + .expect("Content-Type", /json/) + .then((res) => { + res.body.length.should.be.equal(1); + res.body[0].should.have.property("sampleId").and.equal(sampleId); + }); + }); + function defineAttachment(caption) { return { thumbnail: TestData.AttachmentCorrect.thumbnail, - caption: caption || TestData.AttachmentCorrect.caption - } + caption: caption || TestData.AttachmentCorrect.caption, + }; } it("0030: should add a new attachment to this sample", async () => { @@ -84,9 +107,7 @@ describe("2200: Sample: Simple Sample", () => { res.body.should.have .property("thumbnail") .and.equal(attachment.thumbnail); - res.body.should.have - .property("caption") - .and.equal(attachment.caption); + res.body.should.have.property("caption").and.equal(attachment.caption); res.body.should.have .property("ownerGroup") .and.equal(TestData.SampleCorrect.ownerGroup); diff --git a/test/TestData.js b/test/TestData.js index 069cabe00..5a3a5310b 100644 --- a/test/TestData.js +++ b/test/TestData.js @@ -1,25 +1,26 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const { faker } = require("@faker-js/faker"); -const RawTestAccounts = require('../functionalAccounts.json'); -const TestAccounts = Object.fromEntries(RawTestAccounts.map(account => [account.username, account])); +const RawTestAccounts = require("../functionalAccounts.json"); +const TestAccounts = Object.fromEntries( + RawTestAccounts.map((account) => [account.username, account]), +); const TestData = { - - EntryCreatedStatusCode : 201, - EntryValidStatusCode : 200, - CreationForbiddenStatusCode : 403, - DeleteForbiddenStatusCode : 403, - SuccessfulGetStatusCode : 200, - SuccessfulPatchStatusCode : 200, - SuccessfulDeleteStatusCode : 200, - SuccessfulPostStatusCode : 200, + EntryCreatedStatusCode: 201, + EntryValidStatusCode: 200, + CreationForbiddenStatusCode: 403, + DeleteForbiddenStatusCode: 403, + SuccessfulGetStatusCode: 200, + SuccessfulPatchStatusCode: 200, + SuccessfulDeleteStatusCode: 200, + SuccessfulPostStatusCode: 200, BadRequestStatusCode: 400, - AccessForbiddenStatusCode : 403, - UnauthorizedStatusCode : 401, - ConflictStatusCode : 409, - ApplicationErrorStatusCode : 500, - LoginSuccessfulStatusCode : 201, + AccessForbiddenStatusCode: 403, + UnauthorizedStatusCode: 401, + ConflictStatusCode: 409, + ApplicationErrorStatusCode: 500, + LoginSuccessfulStatusCode: 201, //PidPrefix: "github_workflow_testing", PidPrefix: process.env.PID_PREFIX, @@ -52,13 +53,13 @@ const TestData = { instrument: "ESS3-1", start: "2017-07-24T13:56:30.000Z", end: "2017-07-25T13:56:30.000Z", - comment: "Some comment" + comment: "Some comment", }, { instrument: "ESS3-2", start: "2017-07-28T13:56:30.000Z", end: "2017-07-29T13:56:30.000Z", - } + }, ], }, @@ -431,7 +432,10 @@ const TestData = { owner: "Max Novelli", description: "This is a very important sample", sampleCharacteristics: { - chemical_formula: "H2O", + chemical_formula: { + value: "H2O", + unit: "", + }, }, ownerGroup: "ess", accessGroups: ["data scientist", "instrument scientist"],