diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1e2710087b58717b09b70fc06c2caf5bd4145ed2..440e166a2aa5843f06d06b27f243250762ad0710 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,9 @@ +include: +- project: 'eclipse/xfsc/dev-ops/ci-templates' + file: 'helm-build-ci.yaml' + ref: main + + variables: DOCKERFILE: Dockerfile TAG: ${HARBOR_HOST}/${HARBOR_PROJECT}/$SERVICE diff --git a/apps/did-manager/LICENSE b/apps/did-manager/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6bc596c6d9ae158f03739f543ee9d26c4fb5e8d6 --- /dev/null +++ b/apps/did-manager/LICENSE @@ -0,0 +1,636 @@ +GAIA-X "Attestation Manager" + +is the microservice which is responsible for handling the features +related to issuance of credentials of the GAIA-X project. It handles REST +endpoints for Schemas, Credential Definitions and Verifiable Credentials. + +Copyright 2022 Vereign AG + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager incorporates + +"elastic/ecs-winston-format", + +a Node.js package to provide a formatter for the winston logger compatible +with Elastic Common Schema (ECS) logging, which is covered by the following copyright +and permission notice: + +Copyright 2020 Elastic and contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager also incorporates + +"nestjs", + +a progressive Node.js framework for building efficient and scalable server-side applications, +which is covered by the following copyright and permission notice: + +Copyright (c) 2017-2022 Kamil Mysliwiec <https://kamilmysliwiec.com> + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"nestjs/terminus" + +, integrated healthchecks for Nest, which contains the +following copyright and permission notice: + +Copyright (c) 2018-2021 Livio Brunner, Kamil Myśliwiec + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"prisma client" + +,an auto-generated query builder that enables type-safe database access and reduces +boilerplate, which is covered by the following copyright and permission notice: + +Copyright 2019 Johannes Schickling + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager also incorporates + +"types/node" + +,type definitions for Node, which is covered by the following copyright and +permission notice: + +Copyright Microsoft TypeScript, DefinitelyTyped, Alberto Schiabel, +Alvis HT Tang, Andrew Makarov, Benjamin Toueg, Chigozirim C., David Junger, Deividas Bakanas, Eugene Y. Q. Shen, +Hannes Magnusson, Huw, Kelvin Jin, Klaus Meinhardt, Lishude, Mariusz Wiktorczyk, Mohsen Azimi, Nicolas Even, +Nikita Galkin, Parambir Singh, Sebastian Silbermann, Simon Schick, Thomas den Hollander, Wilco Bakker, wwwy3y3, +Samuel Ainsworth, Kyle Uehlein, Thanik Bhongbhibhat, Marcin Kopacz, Trivikram Kamat, Junxiao Shi, Ilia Baryshnikov, +ExE Boss, Piotr Błażejewicz, Anna Henningsen, Victor Perin, Yongsheng Zhang, NodeJS Contributors, +Linus Unnebäck, wafuwafu13, and Matteo Collina. + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"types/express" + +,type definitions for Express (http://expressjs.com), which is covered by the following +copyright and permission notice: + +Copyright Boris Yankov, China Medical University Hospital, Puneet Arora, and Dylan Frankland. + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"types/jest" + +type definitions for Jest, which is covered by the following copyright and permission notice: + +Copyright Asana (https://asana.com)// Ivo Stratev, jwbay, Alexey Svetliakov, Alex Jover Morales, Allan Lukwago, +Ika, Waseem Dahman, Jamie Mason, Douglas Duteil, Ahn, Jeff Lau, Andrew Makarov, Martin Hochel, Sebastian Sebald, +Andy, Antoine Brault, Gregor Stamać, ExE Boss, Alex Bolenok, Mario Beltrán Alarcón, Tony Hallett, Jason Yu, Pawel Fajfer, +Regev Brody, Alexandre Germain, and Adam Jones. + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"class-validator" + +, a tool to allow decorator and non-decorator based validation, + +and + +"class-transformer" + +,a tool to transform plain object to some instance of class and versa, also to serialize / +deserialize object based on criteria, both of which are covered by the following copyright +and permission notice: + +Copyright 2015-2020 TypeStack + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"express" + +,a fast, unopinionated, minimalist web framework for node, which is covered by the following copyright +and permission notice: + +Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca> +Copyright (c) 2013-2014 Roman Shtylman <shtylman+expressjs@gmail.com> +Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com> + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"husky" + +, modern native Git hooks made easy, which is covered by the following copyright and +permission notice: + +Copyright (c) 2021 typicode + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +This Attestation Manager also incorporates + +"joi" + +, the most powerful schema description language and data validator for JavaScript, which is +covered by the following copyright and permission notice: + +Copyright (c) 2012-2020, Sideway. Inc, and project contributors. +Copyright (c) 2012-2014, Walmart. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. +- The names of any contributors may not be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +This Attestation Manager also incorporates + +"liquibase" + +, liquibase is an open-source database-independent library for tracking, managing and applying database schema change, which is covered +by the following copyright and permission notice: + +Copyright (c) Taylor Buckner <taylora.buckner@gmail.com> + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +This Attestation Manager also incorporates + +"moment" + +, a JavaScript date library for parsing, validating, manipulating, and formatting dates, +which is covered by the following copyright and permission notice: + +Copyright (c) JS Foundation and other contributors + +(The MIT License) +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"nats" + +, a Node.js client for the NATS messaging system, which is covered by +the following copyright and permission notice: + +Copyright 2013-2018 The NATS Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager also incorporates + +"pg - node postgres" + +, non-blocking PostgreSQL client for Node.js, which is covered by +the following copyright and permission notice: + +Copyright (c) 2010-2020 Brian Carlson (brian.m.carlson@gmail.com) + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in the +Software without restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This Attestation Manager also incorporates + +"prisma" + +, next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, +SQLite, MongoDB and CockroachDB, which is covered by the following copyright and +permission notice: + +Copyright 2019 Johannes Schickling + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager also incorporates + +"reflect-metadata" + +, prototype for a Metadata Reflection API for ECMAScript, which is covered +by the following copyright and permission notice: + +Copyright 2019 Ron Buckton + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager also incorporates + +"rimraf" + +, a `rm -rf` util for nodejs, which is covered by the following copyright +and permission notice: + +Copyright (c) 2011-2022 Isaac Z. Schlueter and Contributors + +(The ISC License) +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +This Attestation Manager also incorporates + +"rxjs" + +, reactive extensions for JavaScript, which is covered by the following copyright +and permission notice: + +Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +This Attestation Manager also incorporates + +"Swagger UI Express" + +, allows to serve auto-generated swagger-ui generated API docs from express, +based on a swagger.json file, which is covered by the following copyright and +permission notice: + +Copyright (c) 2018 Scott IT London + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +This Attestation Manager also incorporates + +"winston" + +, a logger for just about everything, which is covered by the following copyright and +permission notice: + +Copyright (c) 2010 Charlie Robbins + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +This Attestation Manager also incorporates + +"winston-elasticsearch" + +, an elasticsearch transport for winston, which is covered by the following copyright and +permission notice: + +Copyright (c) 2015 - 2018 Thomas Hoppe. +Copyright (c) 2013 Jacques-Olivier D. Bernier. + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/apps/did-manager/jest.config.js b/apps/did-manager/jest.config.js new file mode 100644 index 0000000000000000000000000000000000000000..ccdd468df2bf90570fb54087fa7dea267814c888 --- /dev/null +++ b/apps/did-manager/jest.config.js @@ -0,0 +1,48 @@ +import { readFileSync } from 'node:fs'; + +const swcConfig = JSON.parse(readFileSync('../../.swcrc', 'utf8')); + +/** @type {import('jest').Config} */ +export default { + moduleFileExtensions: ['js', 'ts'], + testEnvironment: 'node', + transform: { + '^.+\\.(js|ts)$': [ + '@swc/jest', + { + ...swcConfig, + sourceMaps: false, + exclude: [], + swcrc: false, + }, + ], + }, + extensionsToTreatAsEsm: ['.ts'], + moduleNameMapper: { + // ESM modules require `.js` extension to be specified, but Jest doesn't work with them + // Removing `.js` extension from module imports + '^uuid$': 'uuid', + '^(.*)/(.*)\\.js$': '$1/$2', + }, + collectCoverageFrom: ['src/**/*.(t|j)s'], + coverageReporters: + process.env.CI === 'true' + ? ['text-summary', 'json-summary'] + : ['text-summary', 'html'], + coveragePathIgnorePatterns: [ + '<rootDir>/node_modules/', + '<rootDir>/coverage/', + '<rootDir>/dist/', + '__tests__', + '@types', + '.dto.(t|j)s', + '.enum.ts', + '.interface.ts', + '.type.ts', + '.spec.ts', + ], + coverageDirectory: './coverage', + // With v8 coverage provider it's much faster, but + // with this enabled it's not possible to ignore whole files' coverage + coverageProvider: 'v8', +}; diff --git a/apps/did-manager/nest-cli.json b/apps/did-manager/nest-cli.json new file mode 100644 index 0000000000000000000000000000000000000000..b9af737f405bfea055dcb58728c31d912fef06f3 --- /dev/null +++ b/apps/did-manager/nest-cli.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "typeCheck": true, + "builder": { + "type": "swc", + "options": { + "swcrcPath": "../../.swcrc" + } + } + } +} diff --git a/apps/did-manager/package.json b/apps/did-manager/package.json new file mode 100644 index 0000000000000000000000000000000000000000..669db0a31c33af6771afadff248bd04fc512c268 --- /dev/null +++ b/apps/did-manager/package.json @@ -0,0 +1,49 @@ +{ + "name": "@ocm/did-manager", + "version": "1.0.0", + "description": "", + "author": "Konstantin Tsabolov <konstantin.tsabolov@spherity.com>", + "contributors": [ + "Konstantin Tsabolov <konstantin.tsabolov@spherity.com>" + ], + "private": true, + "license": "Apache-2.0", + "type": "module", + "scripts": { + "clean": "rimraf dist coverage *.tsbuildinfo", + "prebuild": "pnpm clean", + "build": "nest build -p tsconfig.production.json", + "start": "nest start --watch --preserveWatchOutput", + "test": "jest" + }, + "dependencies": { + "@nestjs/common": "^10.3.0", + "@nestjs/config": "^3.1.1", + "@nestjs/core": "^10.3.0", + "@nestjs/microservices": "^10.3.0", + "@nestjs/platform-express": "^10.3.0", + "@nestjs/swagger": "^7.2.0", + "@ocm/shared": "workspace:*", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.0", + "express": "^4.17.3", + "joi": "^17.11.0", + "nats": "^2.18.0", + "reflect-metadata": "^0.1.13", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@nestjs/cli": "^10.3.0", + "@nestjs/schematics": "^10.1.0", + "@nestjs/testing": "^10.3.0", + "@swc/cli": "^0.1.62", + "@swc/core": "^1.3.96", + "@swc/jest": "^0.2.29", + "@types/express": "^4.17.21", + "@types/jest": "^29.5.8", + "@types/node": "^20.9.0", + "jest": "^29.7.0", + "rimraf": "^5.0.5", + "typescript": "^5.3.2" + } +} diff --git a/apps/did-manager/src/application.ts b/apps/did-manager/src/application.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e4cea804ed728210217f9abc5f24645c6de7d23 --- /dev/null +++ b/apps/did-manager/src/application.ts @@ -0,0 +1,78 @@ +import type { ConfigType } from '@nestjs/config'; +import type { ClientProvider } from '@nestjs/microservices'; + +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { RouterModule } from '@nestjs/core'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { HealthModule } from '@ocm/shared'; + +import { NATS_CLIENT } from './common/constants.js'; +import { httpConfig } from './config/http.config.js'; +import { natsConfig } from './config/nats.config.js'; +import { validationSchema } from './config/validation.js'; +import { DIDsModule } from './dids/dids.module.js'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + load: [httpConfig, natsConfig], + cache: true, + expandVariables: true, + validationSchema, + validationOptions: { + allowUnknown: true, + abortEarly: true, + }, + }), + + ClientsModule.registerAsync({ + isGlobal: true, + clients: [ + { + name: NATS_CLIENT, + inject: [natsConfig.KEY], + useFactory: (config: ConfigType<typeof natsConfig>) => { + const provider: Required<ClientProvider> = { + transport: Transport.NATS, + options: { + servers: config.url as string, + }, + }; + + if ('user' in config && 'password' in config) { + provider.options.user = config.user as string; + provider.options.pass = config.password as string; + } + + return provider; + }, + }, + ], + }), + + HealthModule.registerAsync({ + inject: [natsConfig.KEY], + useFactory: (config: ConfigType<typeof natsConfig>) => { + const options: Parameters<typeof HealthModule.register>[0] = {}; + + if (config.monitoringUrl) { + options.nats = { + monitoringUrl: config.monitoringUrl as string, + }; + } + + return options; + }, + }), + + DIDsModule, + + RouterModule.register([ + { module: HealthModule, path: '/health' }, + { module: DIDsModule, path: '/dids' }, + ]), + ], +}) +export class Application {} diff --git a/apps/did-manager/src/common/constants.ts b/apps/did-manager/src/common/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..476c13d6650f504d8a2509b73e7c3c03a24d3d6c --- /dev/null +++ b/apps/did-manager/src/common/constants.ts @@ -0,0 +1,2 @@ +export const SERVICE_NAME = 'DID_MANAGER_SERVICE'; +export const NATS_CLIENT = Symbol('NATS_CLIENT'); diff --git a/apps/did-manager/src/config/http.config.ts b/apps/did-manager/src/config/http.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..dbf37be04aac09ddc3eba3e2eaa6db1b8b9ce1f8 --- /dev/null +++ b/apps/did-manager/src/config/http.config.ts @@ -0,0 +1,6 @@ +import { registerAs } from '@nestjs/config'; + +export const httpConfig = registerAs('http', () => ({ + host: process.env.HTTP_HOST || '0.0.0.0', + port: Number(process.env.HTTP_PORT) || 3000, +})); diff --git a/apps/did-manager/src/config/nats.config.ts b/apps/did-manager/src/config/nats.config.ts new file mode 100644 index 0000000000000000000000000000000000000000..194053c2e2e44070e34b8547b4a15819d02d9b75 --- /dev/null +++ b/apps/did-manager/src/config/nats.config.ts @@ -0,0 +1,8 @@ +import { registerAs } from '@nestjs/config'; + +export const natsConfig = registerAs('nats', () => ({ + url: process.env.NATS_URL || 'nats://localhost:4222', + user: process.env.NATS_USER, + password: process.env.NATS_PASSWORD, + monitoringUrl: process.env.NATS_MONITORING_URL || 'http://localhost:8222', +})); diff --git a/apps/did-manager/src/config/validation.ts b/apps/did-manager/src/config/validation.ts new file mode 100644 index 0000000000000000000000000000000000000000..e9abf25ab2e96d0fc30843b3dba5147b4b91074b --- /dev/null +++ b/apps/did-manager/src/config/validation.ts @@ -0,0 +1,11 @@ +import Joi from 'joi'; + +export const validationSchema = Joi.object({ + HTTP_HOST: Joi.string(), + HTTP_PORT: Joi.number(), + + NATS_URL: Joi.string().uri(), + NATS_USER: Joi.string().optional(), + NATS_PASSWORD: Joi.string().optional(), + NATS_MONITORING_URL: Joi.string().uri(), +}); diff --git a/apps/did-manager/src/dids/__tests__/dids.controller.spec.ts b/apps/did-manager/src/dids/__tests__/dids.controller.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..79219ca5067ae088861e121091761f39914b361a --- /dev/null +++ b/apps/did-manager/src/dids/__tests__/dids.controller.spec.ts @@ -0,0 +1,126 @@ +import type { TestingModule } from '@nestjs/testing'; +import type { + EventDidsDidConfiguration, + EventDidsRegisterIndyFromSeed, + EventDidsRegisterIndyFromSeedInput, + EventDidsResolve, +} from '@ocm/shared'; + +import { Test } from '@nestjs/testing'; +import { Subject, of, takeUntil } from 'rxjs'; + +import { NATS_CLIENT } from '../../common/constants.js'; +import { DIDsController } from '../dids.controller.js'; +import { DIDsService } from '../dids.service.js'; + +describe('DIDsController', () => { + const natsClientMock = {}; + + let controller: DIDsController; + let service: DIDsService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [DIDsController], + providers: [ + { provide: NATS_CLIENT, useValue: natsClientMock }, + DIDsService, + ], + }).compile(); + + controller = module.get<DIDsController>(DIDsController); + service = module.get<DIDsService>(DIDsService); + }); + + describe('resolve', () => { + it('should call service.resolve with the correct arguments', (done) => { + const unsubscribe$ = new Subject<void>(); + const tenantId = 'tenantId'; + const did = 'did'; + const expectedResult = {} as EventDidsResolve['data']; + + jest.spyOn(service, 'resolve').mockReturnValueOnce(of(expectedResult)); + + controller + .resolve({ tenantId }, { did }) + .pipe(takeUntil(unsubscribe$)) + .subscribe((result) => { + expect(service.resolve).toHaveBeenCalledWith(tenantId, did); + expect(result).toStrictEqual(expectedResult); + + unsubscribe$.next(); + unsubscribe$.complete(); + + done(); + }); + }); + }); + + describe('registerFromSeed', () => { + it('should call service.registerFromSeed with the correct arguments', (done) => { + const unsubscribe$ = new Subject<void>(); + const tenantId = 'tenantId'; + const seed = 'seed'; + const services: EventDidsRegisterIndyFromSeedInput['services'] = [ + { + identifier: 'serviceId', + type: 'serviceType', + url: 'serviceUrl', + }, + ]; + const expectedResult = {} as EventDidsRegisterIndyFromSeed['data']; + + jest + .spyOn(service, 'registerFromSeed') + .mockReturnValueOnce(of(expectedResult)); + + controller + .registerFromSeed({ tenantId }, { seed, services }) + .pipe(takeUntil(unsubscribe$)) + .subscribe((result) => { + expect(service.registerFromSeed).toHaveBeenCalledWith( + tenantId, + seed, + services, + ); + expect(result).toStrictEqual(expectedResult); + + unsubscribe$.next(); + unsubscribe$.complete(); + + done(); + }); + }); + }); + + describe('getConfiguration', () => { + it('should call service.getConfiguration with the correct arguments', (done) => { + const unsubscribe$ = new Subject<void>(); + const tenantId = 'tenantId'; + const domain = 'domain'; + const expiryTime = 123; + const expectedResult = {} as EventDidsDidConfiguration['data']; + + jest + .spyOn(service, 'getConfiguration') + .mockReturnValueOnce(of(expectedResult)); + + controller + .getConfiguration({ tenantId }, { domain, expiryTime }) + .pipe(takeUntil(unsubscribe$)) + .subscribe((result) => { + expect(service.getConfiguration).toHaveBeenCalledWith( + tenantId, + domain, + expiryTime, + ); + expect(result).toStrictEqual(expectedResult); + + unsubscribe$.next(); + unsubscribe$.complete(); + + done(); + }); + }); + }); +}); diff --git a/apps/did-manager/src/dids/__tests__/dids.module.spec.ts b/apps/did-manager/src/dids/__tests__/dids.module.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..645346937600932948ee624246665486c23ca264 --- /dev/null +++ b/apps/did-manager/src/dids/__tests__/dids.module.spec.ts @@ -0,0 +1,35 @@ +import { ClientsModule } from '@nestjs/microservices'; +import { Test } from '@nestjs/testing'; + +import { NATS_CLIENT } from '../../common/constants.js'; +import { DIDsController } from '../dids.controller.js'; +import { DIDsModule } from '../dids.module.js'; +import { DIDsService } from '../dids.service.js'; + +describe('DIDsModule', () => { + let didsController: DIDsController; + let didsService: DIDsService; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [ + ClientsModule.registerAsync({ + isGlobal: true, + clients: [{ name: NATS_CLIENT, useFactory: () => ({}) }], + }), + DIDsModule, + ], + }).compile(); + + didsController = moduleRef.get<DIDsController>(DIDsController); + didsService = moduleRef.get<DIDsService>(DIDsService); + }); + + it('should be defined', () => { + expect(didsController).toBeDefined(); + expect(didsController).toBeInstanceOf(DIDsController); + + expect(didsService).toBeDefined(); + expect(didsService).toBeInstanceOf(DIDsService); + }); +}); diff --git a/apps/did-manager/src/dids/__tests__/dids.service.spec.ts b/apps/did-manager/src/dids/__tests__/dids.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2cda5e51a148ecc92909961a2193e4a7690a9a57 --- /dev/null +++ b/apps/did-manager/src/dids/__tests__/dids.service.spec.ts @@ -0,0 +1,134 @@ +import type { EventDidsRegisterIndyFromSeedInput } from '@ocm/shared'; + +import { Test } from '@nestjs/testing'; +import { + EventDidsDidConfiguration, + EventDidsRegisterIndyFromSeed, + EventDidsResolve, +} from '@ocm/shared'; +import { Subject, of, takeUntil } from 'rxjs'; + +import { NATS_CLIENT } from '../../common/constants.js'; +import { DIDsService } from '../dids.service.js'; + +describe('DIDsService', () => { + let service: DIDsService; + const natsClientMock = { send: jest.fn() }; + + beforeEach(async () => { + jest.resetAllMocks(); + + const module = await Test.createTestingModule({ + providers: [ + { provide: NATS_CLIENT, useValue: natsClientMock }, + DIDsService, + ], + }).compile(); + + service = module.get<DIDsService>(DIDsService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('resolve', () => { + it('should call natsClient.send with the correct arguments', (done) => { + const unsubscribe$ = new Subject<void>(); + const tenantId = 'tenantId'; + const did = 'did'; + const expectedResult = {} as EventDidsResolve['data']; + + natsClientMock.send.mockReturnValueOnce( + of(new EventDidsResolve(expectedResult, tenantId)), + ); + + service + .resolve(tenantId, did) + .pipe(takeUntil(unsubscribe$)) + .subscribe((result) => { + expect(natsClientMock.send).toHaveBeenCalledWith( + EventDidsResolve.token, + { tenantId, did }, + ); + + expect(result).toStrictEqual(expectedResult); + + unsubscribe$.next(); + unsubscribe$.complete(); + + done(); + }); + }); + }); + + describe('registerFromSeed', () => { + it('should call natsClient.send with the correct arguments', (done) => { + const unsubscribe$ = new Subject<void>(); + const tenantId = 'tenantId'; + const seed = 'seed'; + const services: EventDidsRegisterIndyFromSeedInput['services'] = [ + { + type: 'indy', + url: 'url', + identifier: 'identifier', + }, + ]; + const expectedResult = {} as EventDidsRegisterIndyFromSeed['data']; + + natsClientMock.send.mockReturnValueOnce( + of(new EventDidsRegisterIndyFromSeed(expectedResult, tenantId)), + ); + + service + .registerFromSeed(tenantId, seed, services) + .pipe(takeUntil(unsubscribe$)) + .subscribe((result) => { + expect(natsClientMock.send).toHaveBeenCalledWith( + EventDidsRegisterIndyFromSeed.token, + { tenantId, seed, services }, + ); + + expect(result).toStrictEqual(expectedResult); + + unsubscribe$.next(); + unsubscribe$.complete(); + + done(); + }); + }); + }); + + describe('getConfiguration', () => { + it('should call natsClient.send with the correct arguments', (done) => { + const unsubscribe$ = new Subject<void>(); + const tenantId = 'tenantId'; + const domain = 'domain'; + const expiryTime = 1; + const expectedResult: EventDidsDidConfiguration['data'] = { + entries: [], + }; + + natsClientMock.send.mockReturnValueOnce( + of(new EventDidsDidConfiguration(expectedResult, tenantId)), + ); + + service + .getConfiguration(tenantId, domain, expiryTime) + .pipe(takeUntil(unsubscribe$)) + .subscribe((result) => { + expect(natsClientMock.send).toHaveBeenCalledWith( + EventDidsDidConfiguration.token, + { tenantId, domain, expiryTime }, + ); + + expect(result).toStrictEqual(expectedResult); + + unsubscribe$.next(); + unsubscribe$.complete(); + + done(); + }); + }); + }); +}); diff --git a/apps/did-manager/src/dids/dids.controller.ts b/apps/did-manager/src/dids/dids.controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..de4d1b86e2818ffb15800d61766a4e647cf1d301 --- /dev/null +++ b/apps/did-manager/src/dids/dids.controller.ts @@ -0,0 +1,264 @@ +import { + Body, + Controller, + Get, + HttpCode, + HttpStatus, + Param, + Post, + Query, + UseInterceptors, + UsePipes, + ValidationPipe, +} from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { MultitenancyParams, ResponseFormatInterceptor } from '@ocm/shared'; + +import { DIDsService } from './dids.service.js'; +import { GetConfigurationPayload } from './dto/get-configuration.dto.js'; +import { RegisterFromSeedPayload } from './dto/register-from-seed.dto.js'; +import { ResolveParams } from './dto/resolve.dto.js'; + +@Controller() +@ApiTags('DIDs') +@UsePipes(new ValidationPipe({ transform: true, whitelist: true })) +@UseInterceptors(new ResponseFormatInterceptor()) +export class DIDsController { + public constructor(private readonly service: DIDsService) {} + + @Get(':did') + @ApiOperation({ + summary: 'Resolve DID', + description: 'Resolve DID', + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'DID resolved successfully', + content: { + 'application/json': { + schema: {}, + examples: { + 'DID resolved successfully': { + value: { + statusCode: 200, + message: 'DID resolved successfully', + data: {}, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'DID not found', + content: { + 'application/json': { + schema: {}, + examples: { + 'Tenant not found': { + value: { + statusCode: 404, + message: 'Tenant not found', + }, + }, + 'DID not found': { + value: { + statusCode: 404, + message: 'DID not found', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid DID', + content: { + 'application/json': { + schema: {}, + examples: { + 'Invalid DID': { + value: { + statusCode: 400, + message: 'Invalid DID', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: 'Something went wrong', + content: { + 'application/json': { + schema: {}, + examples: { + 'Something went wrong': { + value: { + statusCode: 500, + message: 'Something went wrong', + error: 'Internal Server Error', + }, + }, + }, + }, + }, + }) + public resolve( + @Query() { tenantId }: MultitenancyParams, + @Param() { did }: ResolveParams, + ) { + return this.service.resolve(tenantId, did); + } + + @Post() + @ApiOperation({ + summary: 'Register DID from seed', + description: 'Register DID from seed', + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'DID registered successfully', + content: { + 'application/json': { + schema: {}, + examples: { + 'DID registered successfully': { + value: { + statusCode: 200, + message: 'DID registered successfully', + data: {}, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid seed', + content: { + 'application/json': { + schema: {}, + examples: { + 'Invalid seed': { + value: { + statusCode: 400, + message: 'Invalid seed', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Tenant not found', + content: { + 'application/json': { + schema: {}, + examples: { + 'Tenant not found': { + value: { + statusCode: 404, + message: 'Tenant not found', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: 'Something went wrong', + content: { + 'application/json': { + schema: {}, + examples: { + 'Something went wrong': { + value: { + statusCode: 500, + message: 'Something went wrong', + error: 'Internal Server Error', + }, + }, + }, + }, + }, + }) + public registerFromSeed( + @Query() { tenantId }: MultitenancyParams, + @Body() { seed, services }: RegisterFromSeedPayload, + ) { + return this.service.registerFromSeed(tenantId, seed, services); + } + + @Post('configuration') + @HttpCode(HttpStatus.OK) + @ApiOperation({ + summary: 'Get DID configuration', + description: 'Get DID configuration', + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'DID configuration fetched successfully', + content: { + 'application/json': { + schema: {}, + examples: { + 'DID configuration fetched successfully': { + value: { + statusCode: 200, + message: 'DID configuration fetched successfully', + data: {}, + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Tenant not found', + content: { + 'application/json': { + schema: {}, + examples: { + 'Tenant not found': { + value: { + statusCode: 404, + message: 'Tenant not found', + }, + }, + }, + }, + }, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: 'Something went wrong', + content: { + 'application/json': { + schema: {}, + examples: { + 'Something went wrong': { + value: { + statusCode: 500, + message: 'Something went wrong', + error: 'Internal Server Error', + }, + }, + }, + }, + }, + }) + public getConfiguration( + @Query() { tenantId }: MultitenancyParams, + @Body() { domain, expiryTime }: GetConfigurationPayload, + ) { + return this.service.getConfiguration(tenantId, domain, expiryTime); + } +} diff --git a/apps/did-manager/src/dids/dids.module.ts b/apps/did-manager/src/dids/dids.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b34aeb0f7c7988a55a5ba368e74b25583993d04 --- /dev/null +++ b/apps/did-manager/src/dids/dids.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; + +import { DIDsController } from './dids.controller.js'; +import { DIDsService } from './dids.service.js'; + +@Module({ + providers: [DIDsService], + controllers: [DIDsController], +}) +export class DIDsModule {} diff --git a/apps/did-manager/src/dids/dids.service.ts b/apps/did-manager/src/dids/dids.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..927325e4a16ad4e584fd79e30b5ea8ab0353373a --- /dev/null +++ b/apps/did-manager/src/dids/dids.service.ts @@ -0,0 +1,58 @@ +import type { + EventDidsDidConfigurationInput, + EventDidsRegisterIndyFromSeedInput, + EventDidsResolveInput, +} from '@ocm/shared'; + +import { Inject, Injectable } from '@nestjs/common'; +import { ClientProxy } from '@nestjs/microservices'; +import { + EventDidsDidConfiguration, + EventDidsRegisterIndyFromSeed, + EventDidsResolve, +} from '@ocm/shared'; +import { map } from 'rxjs'; + +import { NATS_CLIENT } from '../common/constants.js'; + +@Injectable() +export class DIDsService { + public constructor( + @Inject(NATS_CLIENT) private readonly natsClient: ClientProxy, + ) {} + + public resolve(tenantId: string, did: EventDidsResolveInput['did']) { + return this.natsClient + .send< + EventDidsResolve, + EventDidsResolveInput + >(EventDidsResolve.token, { tenantId, did }) + .pipe(map(({ data }) => data)); + } + + public registerFromSeed( + tenantId: string, + seed: EventDidsRegisterIndyFromSeedInput['seed'], + services?: EventDidsRegisterIndyFromSeedInput['services'], + ) { + return this.natsClient + .send< + EventDidsRegisterIndyFromSeed, + EventDidsRegisterIndyFromSeedInput + >(EventDidsRegisterIndyFromSeed.token, { tenantId, seed, services }) + .pipe(map(({ data }) => data)); + } + + public getConfiguration( + tenantId: string, + domain: EventDidsDidConfigurationInput['domain'], + expiryTime: EventDidsDidConfigurationInput['expiryTime'], + ) { + return this.natsClient + .send< + EventDidsDidConfiguration, + EventDidsDidConfigurationInput + >(EventDidsDidConfiguration.token, { tenantId, domain, expiryTime }) + .pipe(map(({ data }) => data)); + } +} diff --git a/apps/did-manager/src/dids/dto/get-configuration.dto.ts b/apps/did-manager/src/dids/dto/get-configuration.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b93f1ac9885984dcc1242a5e89443fa14254596 --- /dev/null +++ b/apps/did-manager/src/dids/dto/get-configuration.dto.ts @@ -0,0 +1,9 @@ +import { IsNumber, IsString } from 'class-validator'; + +export class GetConfigurationPayload { + @IsString() + public domain: string; + + @IsNumber() + public expiryTime: number; +} diff --git a/apps/did-manager/src/dids/dto/register-from-seed.dto.ts b/apps/did-manager/src/dids/dto/register-from-seed.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b06678b68b4af95ab033f660b2f19c5f8547a2a --- /dev/null +++ b/apps/did-manager/src/dids/dto/register-from-seed.dto.ts @@ -0,0 +1,28 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsArray } from 'class-validator'; + +export class RegisterFromSeedPayload { + @IsString() + @ApiProperty({ + description: 'Seed to use for DID generation', + example: '000000000000000000000000Steward1', + }) + public seed: string; + + @IsArray() + @ApiProperty({ + description: 'Services to associate with DID', + example: [ + { + identifier: 'example', + url: 'https://example.com', + type: 'example', + }, + ], + }) + public services?: Array<{ + identifier: string; + url: string; + type: string; + }>; +} diff --git a/apps/did-manager/src/dids/dto/resolve.dto.ts b/apps/did-manager/src/dids/dto/resolve.dto.ts new file mode 100644 index 0000000000000000000000000000000000000000..3b75806484d9c3a26481c477b1596b9000561bcb --- /dev/null +++ b/apps/did-manager/src/dids/dto/resolve.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, Matches } from 'class-validator'; + +export class ResolveParams { + @IsString() + @Matches(/^did:[a-z0-9]+:.+$/) + @ApiProperty({ + description: 'DID to resolve', + example: 'did:example:123', + }) + public did: string; +} diff --git a/apps/did-manager/src/main.ts b/apps/did-manager/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa521d4f2bbaf856fcbbf34f8db9558e730fa760 --- /dev/null +++ b/apps/did-manager/src/main.ts @@ -0,0 +1,55 @@ +/* c8 ignore start */ +import type { ConfigType } from '@nestjs/config'; +import type { MicroserviceOptions, NatsOptions } from '@nestjs/microservices'; + +import { Logger, VersioningType } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { Transport } from '@nestjs/microservices'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; + +import { Application } from './application.js'; +import { httpConfig } from './config/http.config.js'; +import { natsConfig } from './config/nats.config.js'; + +const app = await NestFactory.create(Application); +app.enableCors(); + +const { url, user, password } = app.get(natsConfig.KEY) as ConfigType< + typeof natsConfig +>; + +const microserviceOptions: Required<NatsOptions> = { + transport: Transport.NATS, + options: { + servers: [url], + }, +}; + +if (user && password) { + microserviceOptions.options.user = user; + microserviceOptions.options.pass = password; +} + +app.connectMicroservice<MicroserviceOptions>(microserviceOptions); + +app.enableVersioning({ + defaultVersion: ['1'], + type: VersioningType.URI, +}); + +const swaggerConfig = new DocumentBuilder() + .setTitle('Gaia-X OCM DID Manager API') + .setDescription('API documentation for Gaia-X OCM DID Manager') + .setVersion('1.0') + .build(); + +const document = SwaggerModule.createDocument(app, swaggerConfig); + +SwaggerModule.setup('/', app, document); +await app.startAllMicroservices(); + +const { host, port } = app.get(httpConfig.KEY) as ConfigType<typeof httpConfig>; +await app.listen(port as number, host as string); + +Logger.log(`Application is running on: ${await app.getUrl()}`); +/* c8 ignore stop */ diff --git a/apps/did-manager/tsconfig.build.json b/apps/did-manager/tsconfig.build.json new file mode 100644 index 0000000000000000000000000000000000000000..3e5ab438230b6cbd30a5825fc562c485a89ff95d --- /dev/null +++ b/apps/did-manager/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./dist", + "rootDir": "./src" + }, + "exclude": ["node_modules", "**/test", "**/dist", "**/*spec.ts"] +} diff --git a/apps/did-manager/tsconfig.json b/apps/did-manager/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..4082f16a5d91ce6f21a9092b14170eeecc8f1d75 --- /dev/null +++ b/apps/did-manager/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/apps/did-manager/tsconfig.production.json b/apps/did-manager/tsconfig.production.json new file mode 100644 index 0000000000000000000000000000000000000000..45f85dfe5daf11a59e2fac464fa15940a2f50200 --- /dev/null +++ b/apps/did-manager/tsconfig.production.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.production.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./dist", + "rootDir": "./src" + }, + "exclude": ["node_modules", "**/test", "**/dist", "**/*spec.ts"] +} diff --git a/apps/shared/src/events/credentialEvents.ts b/apps/shared/src/events/credentialEvents.ts index e52cdbf812ad908da705d976dbf5eaab48be7ad3..a3980307cdaf6f99c66dbd35a8069cfafb6e1a13 100644 --- a/apps/shared/src/events/credentialEvents.ts +++ b/apps/shared/src/events/credentialEvents.ts @@ -50,6 +50,27 @@ export class EventAnonCredsCredentialsGetById extends BaseEvent<CredentialExchan } } +export type EventDidcommAnonCredsCredentialsAcceptOfferInput = BaseEventInput<{ + credentialId: string; +}>; +export class EventDidcommAnonCredsCredentialsAcceptOffer extends BaseEvent<CredentialExchangeRecord> { + public static token = 'didcomm.anoncreds.credentials.acceptOffer'; + + public get instance() { + return JsonTransformer.fromJSON(this.data, CredentialExchangeRecord); + } + + public static fromEvent(e: EventDidcommAnonCredsCredentialsOffer) { + return new EventDidcommAnonCredsCredentialsOffer( + e.data, + e.tenantId, + e.id, + e.type, + e.timestamp, + ); + } +} + export type EventDidcommAnonCredsCredentialsOfferInput = BaseEventInput<{ connectionId: string; credentialDefinitionId: string; diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts index b4a7841072c40c3f344c31e35f09e6b8f1ffe9ec..963794535c06317035b136e331ee1883c053389e 100644 --- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts +++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.controller.ts @@ -15,6 +15,8 @@ import { EventAnonCredsCredentialsGetAllInput, EventAnonCredsCredentialsGetById, EventAnonCredsCredentialsGetByIdInput, + EventDidcommAnonCredsCredentialsAcceptOffer, + EventDidcommAnonCredsCredentialsAcceptOfferInput, EventDidcommAnonCredsCredentialsOffer, EventDidcommAnonCredsCredentialsOfferInput, EventDidcommAnonCredsCredentialsOfferToSelf, @@ -97,6 +99,16 @@ export class AnonCredsCredentialsController { ); } + @MessagePattern(EventDidcommAnonCredsCredentialsAcceptOffer.token) + public async acceptOffer( + options: EventDidcommAnonCredsCredentialsAcceptOfferInput, + ): Promise<EventDidcommAnonCredsCredentialsAcceptOffer> { + return new EventDidcommAnonCredsCredentialsAcceptOffer( + await this.credentialsService.acceptOffer(options), + options.tenantId, + ); + } + @MessagePattern(EventDidcommAnonCredsCredentialsOffer.token) public async offer( options: EventDidcommAnonCredsCredentialsOfferInput, diff --git a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts index 1230d74b9f671c2bfc2161760572c4a13815ed0f..103f0c2ba80d4795a13fe4d0de965991cc2ae266 100644 --- a/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts +++ b/apps/ssi-abstraction/src/agent/anoncredsCredentials/anoncredsCredentials.service.ts @@ -16,6 +16,8 @@ import type { EventAnonCredsCredentialsDeleteByIdInput, EventAnonCredsCredentialsGetAllInput, EventAnonCredsCredentialsGetByIdInput, + EventDidcommAnonCredsCredentialsAcceptOffer, + EventDidcommAnonCredsCredentialsAcceptOfferInput, EventDidcommAnonCredsCredentialsOffer, EventDidcommAnonCredsCredentialsOfferInput, EventDidcommAnonCredsCredentialsOfferToSelf, @@ -24,8 +26,8 @@ import type { import { AutoAcceptCredential, - CredentialState, CredentialEventTypes, + CredentialState, } from '@credo-ts/core'; import { GenericRecord } from '@credo-ts/core/build/modules/generic-records/repository/GenericRecord.js'; import { Injectable } from '@nestjs/common'; @@ -149,6 +151,19 @@ export class AnonCredsCredentialsService { }); } + public async acceptOffer({ + tenantId, + credentialId, + }: EventDidcommAnonCredsCredentialsAcceptOfferInput): Promise< + EventDidcommAnonCredsCredentialsAcceptOffer['data'] + > { + return this.withTenantService.invoke(tenantId, (t) => + t.credentials.acceptOffer({ + credentialRecordId: credentialId, + }), + ); + } + public async offer({ tenantId, connectionId, diff --git a/apps/ssi-abstraction/test/anoncredsCredentials.e2e-spec.ts b/apps/ssi-abstraction/test/anoncredsCredentials.e2e-spec.ts index 672778b11dc0f226e4c035975d43ec3c9cd32f62..e90864892823e43c0af05a087c5e4c3ee1486b2f 100644 --- a/apps/ssi-abstraction/test/anoncredsCredentials.e2e-spec.ts +++ b/apps/ssi-abstraction/test/anoncredsCredentials.e2e-spec.ts @@ -1,28 +1,36 @@ import type { INestApplication } from '@nestjs/common'; import type { ClientProxy } from '@nestjs/microservices'; import type { - EventAnonCredsCredentialRequestGetAllInput, - EventAnonCredsCredentialsGetAllInput, - EventAnonCredsCredentialsGetByIdInput, - EventDidcommAnonCredsCredentialsOfferToSelfInput, EventAnonCredsCredentialOfferGetAllInput, EventAnonCredsCredentialOfferGetByIdInput, + EventAnonCredsCredentialRequestGetAllInput, EventAnonCredsCredentialRequestGetByIdInput, EventAnonCredsCredentialsDeleteByIdInput, + EventAnonCredsCredentialsGetAllInput, + EventAnonCredsCredentialsGetByIdInput, + EventDidcommAnonCredsCredentialsAcceptOffer, + EventDidcommAnonCredsCredentialsAcceptOfferInput, + EventDidcommAnonCredsCredentialsOfferInput, + EventDidcommAnonCredsCredentialsOfferToSelfInput, } from '@ocm/shared'; -import { AutoAcceptCredential, CredentialExchangeRecord } from '@credo-ts/core'; +import { + AutoAcceptCredential, + CredentialExchangeRecord, + CredentialState, +} from '@credo-ts/core'; import { ClientsModule, Transport } from '@nestjs/microservices'; import { Test } from '@nestjs/testing'; import { - EventAnonCredsCredentialsDeleteById, EventAnonCredsCredentialOfferGetAll, EventAnonCredsCredentialOfferGetById, EventAnonCredsCredentialRequestGetAll, EventAnonCredsCredentialRequestGetById, + EventAnonCredsCredentialsDeleteById, EventAnonCredsCredentialsGetAll, EventAnonCredsCredentialsGetById, EventAnonCredsProofsDeleteById, + EventDidcommAnonCredsCredentialsOffer, EventDidcommAnonCredsCredentialsOfferToSelf, } from '@ocm/shared'; import { randomBytes } from 'crypto'; @@ -50,6 +58,7 @@ describe('Credentials', () => { let issuerDid: string; let credentialDefinitionId: string; + let connectionId: string; beforeAll(async () => { const moduleRef = await Test.createTestingModule({ @@ -111,6 +120,18 @@ describe('Credentials', () => { }); credentialDefinitionId = cdi; + + const connectionService = app.get(ConnectionsService); + const { invitationUrl } = await connectionService.createInvitation({ + tenantId, + }); + + const { id: cId } = await connectionService.receiveInvitationFromUrl({ + tenantId, + invitationUrl, + }); + + connectionId = cId; }); afterAll(async () => { @@ -197,6 +218,45 @@ describe('Credentials', () => { expect(eventInstance.instance).toEqual(null); }); + it(EventDidcommAnonCredsCredentialsOffer.token, async () => { + const attributes = [ + { name: 'Name', value: 'Berend' }, + { name: 'Age', value: '25' }, + ]; + + const response$ = client.send< + EventDidcommAnonCredsCredentialsOffer, + EventDidcommAnonCredsCredentialsOfferInput + >(EventDidcommAnonCredsCredentialsOffer.token, { + tenantId, + connectionId, + attributes, + credentialDefinitionId, + }); + + const response = await firstValueFrom(response$); + const eventInstance = + EventDidcommAnonCredsCredentialsOffer.fromEvent(response); + + await new Promise((r) => setTimeout(r, 2000)); + + const acceptResponse$ = client.send< + EventDidcommAnonCredsCredentialsAcceptOffer, + EventDidcommAnonCredsCredentialsAcceptOfferInput + >(EventDidcommAnonCredsCredentialsOffer.token, { + tenantId, + credentialId: eventInstance.instance.id, + }); + + const acceptResponse = await firstValueFrom(acceptResponse$); + const acceptEventInstance = + EventAnonCredsCredentialsGetById.fromEvent(acceptResponse); + + expect(acceptEventInstance.instance).toMatchObject({ + state: CredentialState.RequestSent, + }); + }); + it(EventDidcommAnonCredsCredentialsOfferToSelf.token, async () => { const attributes = [ { name: 'Name', value: 'Berend' }, diff --git a/docker-compose.yml b/docker-compose.yml index 6422563cd76462d503bb270acc6dc38737663c28..6f1b0df4da1d2cf847f9e799426e5de5f7f7c44b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -137,3 +137,20 @@ services: - '4004:3000' depends_on: - nats + + did-manager: + build: + args: + - SERVICE=did-manager + init: true + environment: + HTTP_HOST: 0.0.0.0 + HTTP_PORT: 3000 + NATS_URL: nats://nats:4222 + NATS_USER: nats_user + NATS_PASSWORD: Rw+dYIymAQm9H6ELLNwSuGo1812jqQ== + NATS_MONITORING_URL: http://nats:8222 + ports: + - '4006:3000' + depends_on: + - nats diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84ed5d7c81df6e198a90e030b08b9b2dfe2af5c7..5fa7773ca395be03ce017e45c732830a24287119 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -248,6 +248,88 @@ importers: specifier: ^5.3.2 version: 5.3.3 + apps/did-manager: + dependencies: + '@nestjs/common': + specifier: ^10.3.0 + version: 10.3.0(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/config': + specifier: ^3.1.1 + version: 3.1.1(@nestjs/common@10.3.0)(reflect-metadata@0.1.14) + '@nestjs/core': + specifier: ^10.3.0 + version: 10.3.0(@nestjs/common@10.3.0)(@nestjs/microservices@10.3.0)(@nestjs/platform-express@10.3.0)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/microservices': + specifier: ^10.3.0 + version: 10.3.0(@nestjs/common@10.3.0)(@nestjs/core@10.3.0)(nats@2.19.0)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/platform-express': + specifier: ^10.3.0 + version: 10.3.0(@nestjs/common@10.3.0)(@nestjs/core@10.3.0) + '@nestjs/swagger': + specifier: ^7.2.0 + version: 7.2.0(@nestjs/common@10.3.0)(@nestjs/core@10.3.0)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) + '@ocm/shared': + specifier: workspace:* + version: link:../shared + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 + class-validator: + specifier: ^0.14.0 + version: 0.14.1 + express: + specifier: ^4.17.3 + version: 4.18.2 + joi: + specifier: ^17.11.0 + version: 17.11.1 + nats: + specifier: ^2.18.0 + version: 2.19.0 + reflect-metadata: + specifier: ^0.1.13 + version: 0.1.14 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + devDependencies: + '@nestjs/cli': + specifier: ^10.3.0 + version: 10.3.0(@swc/cli@0.1.63)(@swc/core@1.3.103) + '@nestjs/schematics': + specifier: ^10.1.0 + version: 10.1.0(chokidar@3.5.3)(typescript@5.3.3) + '@nestjs/testing': + specifier: ^10.3.0 + version: 10.3.0(@nestjs/common@10.3.0)(@nestjs/core@10.3.0)(@nestjs/microservices@10.3.0)(@nestjs/platform-express@10.3.0) + '@swc/cli': + specifier: ^0.1.62 + version: 0.1.63(@swc/core@1.3.103) + '@swc/core': + specifier: ^1.3.96 + version: 1.3.103 + '@swc/jest': + specifier: ^0.2.29 + version: 0.2.29(@swc/core@1.3.103) + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/jest': + specifier: ^29.5.8 + version: 29.5.11 + '@types/node': + specifier: ^20.9.0 + version: 20.11.5 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.11.5)(ts-node@10.9.2) + rimraf: + specifier: ^5.0.5 + version: 5.0.5 + typescript: + specifier: ^5.3.2 + version: 5.3.3 + apps/proof-manager: dependencies: '@nestjs/common':