From 88a37da758339bd5d5833dd78627c94834f84be9 Mon Sep 17 00:00:00 2001 From: vineela-afk Date: Thu, 28 Mar 2024 11:56:49 +0530 Subject: [PATCH] Added the unlink and packaging --- .../Dockerfile => Dockerfile | 9 +- main.py | 10 + openg2p-spar-mapper-api/.copier-answers.yml | 16 - openg2p-spar-mapper-api/CODE-OF-CONDUCT.md | 114 ------ openg2p-spar-mapper-api/CONTRIBUTING.md | 2 - openg2p-spar-mapper-api/LICENSE | 373 ------------------ openg2p-spar-mapper-api/README.md | 13 - openg2p-spar-mapper-api/main.py | 9 - .../g2pconnect/async_mapper_controller.py | 178 --------- .../src/schemas/__init__.py | 0 openg2p-spar-mapper-api/tests/__init__.py | 0 openg2p-spar-mapper-api/tox.ini | 17 - .../pyproject.toml => pyproject.toml | 8 +- .../openg2p_spar_mapper_api}/.dockerignore | 0 .../openg2p_spar_mapper_api}/.editorconfig | 0 .../openg2p_spar_mapper_api}/.gitignore | 0 .../.pre-commit-config.yaml | 0 .../openg2p_spar_mapper_api}/.ruff.toml | 0 .../openg2p_spar_mapper_api}/__init__.py | 0 .../openg2p_spar_mapper_api}/app.py | 5 +- .../openg2p_spar_mapper_api}/config.py | 2 +- .../controllers}/__init__.py | 0 .../controllers/g2pconnect/__init__.py | 0 .../g2pconnect/async_mapper_controller.py | 170 ++++++++ .../g2pconnect/sync_mapper_controller.py | 23 +- .../errors}/__init__.py | 0 .../errors/error_codes.py | 0 .../models/__init__.py | 0 .../models/id_fa_mapping.py | 0 .../schemas}/__init__.py | 0 .../services/__init__.py | 0 .../services/exceptions.py | 12 +- .../services/id_fa_mapping_validations.py | 41 +- .../services/mapper.py | 76 ++++ .../services/request_helper.py | 0 .../services/request_validations.py | 8 + .../services/response_helper.py | 53 ++- 37 files changed, 394 insertions(+), 745 deletions(-) rename openg2p-spar-mapper-api/Dockerfile => Dockerfile (54%) create mode 100755 main.py delete mode 100644 openg2p-spar-mapper-api/.copier-answers.yml delete mode 100644 openg2p-spar-mapper-api/CODE-OF-CONDUCT.md delete mode 100644 openg2p-spar-mapper-api/CONTRIBUTING.md delete mode 100644 openg2p-spar-mapper-api/LICENSE delete mode 100644 openg2p-spar-mapper-api/README.md delete mode 100755 openg2p-spar-mapper-api/main.py delete mode 100644 openg2p-spar-mapper-api/src/controllers/g2pconnect/async_mapper_controller.py delete mode 100644 openg2p-spar-mapper-api/src/schemas/__init__.py delete mode 100644 openg2p-spar-mapper-api/tests/__init__.py delete mode 100644 openg2p-spar-mapper-api/tox.ini rename openg2p-spar-mapper-api/pyproject.toml => pyproject.toml (73%) rename {openg2p-spar-mapper-api => src/openg2p_spar_mapper_api}/.dockerignore (100%) rename {openg2p-spar-mapper-api => src/openg2p_spar_mapper_api}/.editorconfig (100%) rename {openg2p-spar-mapper-api => src/openg2p_spar_mapper_api}/.gitignore (100%) rename {openg2p-spar-mapper-api => src/openg2p_spar_mapper_api}/.pre-commit-config.yaml (100%) rename {openg2p-spar-mapper-api => src/openg2p_spar_mapper_api}/.ruff.toml (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/__init__.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/app.py (99%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/config.py (99%) rename {openg2p-spar-mapper-api => src/openg2p_spar_mapper_api/controllers}/__init__.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/controllers/g2pconnect/__init__.py (100%) create mode 100644 src/openg2p_spar_mapper_api/controllers/g2pconnect/async_mapper_controller.py rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/controllers/g2pconnect/sync_mapper_controller.py (81%) rename {openg2p-spar-mapper-api/src/controllers => src/openg2p_spar_mapper_api/errors}/__init__.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/errors/error_codes.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/models/__init__.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/models/id_fa_mapping.py (100%) rename {openg2p-spar-mapper-api/src/errors => src/openg2p_spar_mapper_api/schemas}/__init__.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/__init__.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/exceptions.py (76%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/id_fa_mapping_validations.py (76%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/mapper.py (82%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/request_helper.py (100%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/request_validations.py (79%) rename {openg2p-spar-mapper-api/src => src/openg2p_spar_mapper_api}/services/response_helper.py (83%) diff --git a/openg2p-spar-mapper-api/Dockerfile b/Dockerfile similarity index 54% rename from openg2p-spar-mapper-api/Dockerfile rename to Dockerfile index 0c30ad0..8381912 100644 --- a/openg2p-spar-mapper-api/Dockerfile +++ b/Dockerfile @@ -17,11 +17,14 @@ RUN chown -R ${container_user}:${container_user_group} /app USER ${container_user} ADD --chown=${container_user}:${container_user_group} . /app/src -ADD --chown=${container_user}:${container_user_group} main.py /app +ADD --chown=${container_user}:${container_user_group} openg2p-spar-mapper-api/main.py /app RUN python3 -m venv venv \ && . ./venv/bin/activate -RUN python3 -m pip install ./src - +RUN python3 -m pip install \ + git+https://github.com/openg2p/openg2p-fastapi-common.git@develop\#egg=openg2p-fastapi-common\&subdirectory=openg2p-fastapi-common \ + git+https://github.com/openg2p/openg2p-fastapi-common.git@develop\#egg=openg2p-fastapi-auth\&subdirectory=openg2p-fastapi-auth \ + git+https://github.com/openg2p/openg2p-fastapi-common.git@develop\#egg=openg2p-common-g2pconnect-id-mapper\&subdirectory=openg2p-common-g2pconnect-id-mapper \ + ./src/openg2p-spar-mapper-api CMD python3 main.py migrate; \ python3 main.py run diff --git a/main.py b/main.py new file mode 100755 index 0000000..23b7b1d --- /dev/null +++ b/main.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +# ruff: noqa: I001 + +from openg2p_spar_mapper_api.app import Initializer +# from openg2p_fastapi_common.ping import PingInitializer + +main_init = Initializer() + +main_init.main() \ No newline at end of file diff --git a/openg2p-spar-mapper-api/.copier-answers.yml b/openg2p-spar-mapper-api/.copier-answers.yml deleted file mode 100644 index b98a252..0000000 --- a/openg2p-spar-mapper-api/.copier-answers.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Do NOT update manually; changes here will be overwritten by Copier -_commit: 44dde56 -_src_path: https://github.com/openg2p/openg2p-fastapi-template -github_ci_docker_build: true -github_ci_openapi_publish: true -github_ci_precommit: true -github_ci_pypi_publish: true -github_ci_tests: true -github_ci_tests_codecov: true -module_name: openg2p_spar_mapper_api -org_name: OpenG2P -org_slug: OpenG2P -package_name: openg2p-spar-mapper-api -repo_name: openg2p-spar-mapper-api -repo_slug: openg2p-spar-mapper-api - diff --git a/openg2p-spar-mapper-api/CODE-OF-CONDUCT.md b/openg2p-spar-mapper-api/CODE-OF-CONDUCT.md deleted file mode 100644 index e1949e1..0000000 --- a/openg2p-spar-mapper-api/CODE-OF-CONDUCT.md +++ /dev/null @@ -1,114 +0,0 @@ -# Code of Conduct - -## Contributor Covenant Code of Conduct - -### Preamble - -OpenG2P was created to foster an open, innovative and inclusive community around open source & open standard. -To clarify expected behaviour in our communities we have adopted the Contributor Covenant. This code of -conduct has been adopted by many other open source communities and we feel it expresses our values well. - -### Our Pledge - -We as members, contributors, and leaders pledge to make participation in our community a harassment-free -experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex -characteristics, gender identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy -community. - -### Our Standards - -Examples of behavior that contributes to a positive environment for our community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or advances of any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email address, without their explicit - permission -- Other conduct which could reasonably be considered inappropriate in a professional setting - -### Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will -take appropriate and fair corrective action in response to any behavior that they deem inappropriate, -threatening, offensive, or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki -edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate -reasons for moderation decisions when appropriate. - -### Scope - -This Code of Conduct applies within all community spaces, and also applies when an individual is officially -representing the community in public spaces. Examples of representing our community include using an official -e-mail address, posting via an official social media account, or acting as an appointed representative at an -online or offline event. - -### Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders -responsible for enforcement at \[INSERT CONTACT METHOD]. All complaints will be reviewed and investigated -promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the reporter of any incident. - -### Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining the consequences for any action -they deem in violation of this Code of Conduct: - -#### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in -the community. - -**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the -violation and an explanation of why the behavior was inappropriate. A public apology may be requested. - -#### 2. Warning - -**Community Impact**: A violation through a single incident or series of actions. - -**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, -including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. -This includes avoiding interactions in community spaces as well as external channels like social media. -Violating these terms may lead to a temporary or permanent ban. - -#### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a -specified period of time. No public or private interaction with the people involved, including unsolicited -interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may -lead to a permanent ban. - -#### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained -inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of -individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the community. - -### Attribution - -This Code of Conduct is adapted from the -[Contributor Covenant version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). - -For answers to common questions about this code of conduct, see the -[FAQ](https://www.contributor-covenant.org/faq). Translations are available -[here](https://www.contributor-covenant.org/translations). diff --git a/openg2p-spar-mapper-api/CONTRIBUTING.md b/openg2p-spar-mapper-api/CONTRIBUTING.md deleted file mode 100644 index c187f93..0000000 --- a/openg2p-spar-mapper-api/CONTRIBUTING.md +++ /dev/null @@ -1,2 +0,0 @@ -Refer to contribution guidelines -[here](https://github.com/OpenG2P/openg2p-documentation/blob/1.0.0/community/contributing-to-openg2p.md). diff --git a/openg2p-spar-mapper-api/LICENSE b/openg2p-spar-mapper-api/LICENSE deleted file mode 100644 index a612ad9..0000000 --- a/openg2p-spar-mapper-api/LICENSE +++ /dev/null @@ -1,373 +0,0 @@ -Mozilla Public License Version 2.0 -================================== - -1. Definitions --------------- - -1.1. "Contributor" - means each individual or legal entity that creates, contributes to - the creation of, or owns Covered Software. - -1.2. "Contributor Version" - means the combination of the Contributions of others (if any) used - by a Contributor and that particular Contributor's Contribution. - -1.3. "Contribution" - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - means Source Code Form to which the initial Contributor has attached - the notice in Exhibit A, the Executable Form of such Source Code - Form, and Modifications of such Source Code Form, in each case - including portions thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - (a) that the initial Contributor has attached the notice described - in Exhibit B to the Covered Software; or - - (b) that the Covered Software was made available under the terms of - version 1.1 or earlier of the License, but not also under the - terms of a Secondary License. - -1.6. "Executable Form" - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - means a work that combines Covered Software with other material, in - a separate file or files, that is not Covered Software. - -1.8. "License" - means this document. - -1.9. "Licensable" - means having the right to grant, to the maximum extent possible, - whether at the time of the initial grant or subsequently, any and - all of the rights conveyed by this License. - -1.10. "Modifications" - means any of the following: - - (a) any file in Source Code Form that results from an addition to, - deletion from, or modification of the contents of Covered - Software; or - - (b) any new file in Source Code Form that contains any Covered - Software. - -1.11. "Patent Claims" of a Contributor - means any patent claim(s), including without limitation, method, - process, and apparatus claims, in any patent Licensable by such - Contributor that would be infringed, but for the grant of the - License, by the making, using, selling, offering for sale, having - made, import, or transfer of either its Contributions or its - Contributor Version. - -1.12. "Secondary License" - means either the GNU General Public License, Version 2.0, the GNU - Lesser General Public License, Version 2.1, the GNU Affero General - Public License, Version 3.0, or any later versions of those - licenses. - -1.13. "Source Code Form" - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that - controls, is controlled by, or is under common control with You. For - purposes of this definition, "control" means (a) the power, direct - or indirect, to cause the direction or management of such entity, - whether by contract or otherwise, or (b) ownership of more than - fifty percent (50%) of the outstanding shares or beneficial - ownership of such entity. - -2. License Grants and Conditions --------------------------------- - -2.1. Grants - -Each Contributor hereby grants You a world-wide, royalty-free, -non-exclusive license: - -(a) under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or - as part of a Larger Work; and - -(b) under Patent Claims of such Contributor to make, use, sell, offer - for sale, have made, import, and otherwise transfer either its - Contributions or its Contributor Version. - -2.2. Effective Date - -The licenses granted in Section 2.1 with respect to any Contribution -become effective for each Contribution on the date the Contributor first -distributes such Contribution. - -2.3. Limitations on Grant Scope - -The licenses granted in this Section 2 are the only rights granted under -this License. No additional rights or licenses will be implied from the -distribution or licensing of Covered Software under this License. -Notwithstanding Section 2.1(b) above, no patent license is granted by a -Contributor: - -(a) for any code that a Contributor has removed from Covered Software; - or - -(b) for infringements caused by: (i) Your and any other third party's - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - -(c) under Patent Claims infringed by Covered Software in the absence of - its Contributions. - -This License does not grant any rights in the trademarks, service marks, -or logos of any Contributor (except as may be necessary to comply with -the notice requirements in Section 3.4). - -2.4. Subsequent Licenses - -No Contributor makes additional grants as a result of Your choice to -distribute the Covered Software under a subsequent version of this -License (see Section 10.2) or under the terms of a Secondary License (if -permitted under the terms of Section 3.3). - -2.5. Representation - -Each Contributor represents that the Contributor believes its -Contributions are its original creation(s) or it has sufficient rights -to grant the rights to its Contributions conveyed by this License. - -2.6. Fair Use - -This License is not intended to limit any rights You have under -applicable copyright doctrines of fair use, fair dealing, or other -equivalents. - -2.7. Conditions - -Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted -in Section 2.1. - -3. Responsibilities -------------------- - -3.1. Distribution of Source Form - -All distribution of Covered Software in Source Code Form, including any -Modifications that You create or to which You contribute, must be under -the terms of this License. You must inform recipients that the Source -Code Form of the Covered Software is governed by the terms of this -License, and how they can obtain a copy of this License. You may not -attempt to alter or restrict the recipients' rights in the Source Code -Form. - -3.2. Distribution of Executable Form - -If You distribute Covered Software in Executable Form then: - -(a) such Covered Software must also be made available in Source Code - Form, as described in Section 3.1, and You must inform recipients of - the Executable Form how they can obtain a copy of such Source Code - Form by reasonable means in a timely manner, at a charge no more - than the cost of distribution to the recipient; and - -(b) You may distribute such Executable Form under the terms of this - License, or sublicense it under different terms, provided that the - license for the Executable Form does not attempt to limit or alter - the recipients' rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - -You may create and distribute a Larger Work under terms of Your choice, -provided that You also comply with the requirements of this License for -the Covered Software. If the Larger Work is a combination of Covered -Software with a work governed by one or more Secondary Licenses, and the -Covered Software is not Incompatible With Secondary Licenses, this -License permits You to additionally distribute such Covered Software -under the terms of such Secondary License(s), so that the recipient of -the Larger Work may, at their option, further distribute the Covered -Software under the terms of either this License or such Secondary -License(s). - -3.4. Notices - -You may not remove or alter the substance of any license notices -(including copyright notices, patent notices, disclaimers of warranty, -or limitations of liability) contained within the Source Code Form of -the Covered Software, except that You may alter any license notices to -the extent required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - -You may choose to offer, and to charge a fee for, warranty, support, -indemnity or liability obligations to one or more recipients of Covered -Software. However, You may do so only on Your own behalf, and not on -behalf of any Contributor. You must make it absolutely clear that any -such warranty, support, indemnity, or liability obligation is offered by -You alone, and You hereby agree to indemnify every Contributor for any -liability incurred by such Contributor as a result of warranty, support, -indemnity or liability terms You offer. You may include additional -disclaimers of warranty and limitations of liability specific to any -jurisdiction. - -4. Inability to Comply Due to Statute or Regulation ---------------------------------------------------- - -If it is impossible for You to comply with any of the terms of this -License with respect to some or all of the Covered Software due to -statute, judicial order, or regulation then You must: (a) comply with -the terms of this License to the maximum extent possible; and (b) -describe the limitations and the code they affect. Such description must -be placed in a text file included with all distributions of the Covered -Software under this License. Except to the extent prohibited by statute -or regulation, such description must be sufficiently detailed for a -recipient of ordinary skill to be able to understand it. - -5. Termination --------------- - -5.1. The rights granted under this License will terminate automatically -if You fail to comply with any of its terms. However, if You become -compliant, then the rights granted under this License from a particular -Contributor are reinstated (a) provisionally, unless and until such -Contributor explicitly and finally terminates Your grants, and (b) on an -ongoing basis, if such Contributor fails to notify You of the -non-compliance by some reasonable means prior to 60 days after You have -come back into compliance. Moreover, Your grants from a particular -Contributor are reinstated on an ongoing basis if such Contributor -notifies You of the non-compliance by some reasonable means, this is the -first time You have received notice of non-compliance with this License -from such Contributor, and You become compliant prior to 30 days after -Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent -infringement claim (excluding declaratory judgment actions, -counter-claims, and cross-claims) alleging that a Contributor Version -directly or indirectly infringes any patent, then the rights granted to -You by any and all Contributors for the Covered Software under Section -2.1 of this License shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all -end user license agreements (excluding distributors and resellers) which -have been validly granted by You or Your distributors under this License -prior to termination shall survive termination. - -************************************************************************ -* * -* 6. Disclaimer of Warranty * -* ------------------------- * -* * -* Covered Software is provided under this License on an "as is" * -* basis, without warranty of any kind, either expressed, implied, or * -* statutory, including, without limitation, warranties that the * -* Covered Software is free of defects, merchantable, fit for a * -* particular purpose or non-infringing. The entire risk as to the * -* quality and performance of the Covered Software is with You. * -* Should any Covered Software prove defective in any respect, You * -* (not any Contributor) assume the cost of any necessary servicing, * -* repair, or correction. This disclaimer of warranty constitutes an * -* essential part of this License. No use of any Covered Software is * -* authorized under this License except under this disclaimer. * -* * -************************************************************************ - -************************************************************************ -* * -* 7. Limitation of Liability * -* -------------------------- * -* * -* Under no circumstances and under no legal theory, whether tort * -* (including negligence), contract, or otherwise, shall any * -* Contributor, or anyone who distributes Covered Software as * -* permitted above, be liable to You for any direct, indirect, * -* special, incidental, or consequential damages of any character * -* including, without limitation, damages for lost profits, loss of * -* goodwill, work stoppage, computer failure or malfunction, or any * -* and all other commercial damages or losses, even if such party * -* shall have been informed of the possibility of such damages. This * -* limitation of liability shall not apply to liability for death or * -* personal injury resulting from such party's negligence to the * -* extent applicable law prohibits such limitation. Some * -* jurisdictions do not allow the exclusion or limitation of * -* incidental or consequential damages, so this exclusion and * -* limitation may not apply to You. * -* * -************************************************************************ - -8. Litigation -------------- - -Any litigation relating to this License may be brought only in the -courts of a jurisdiction where the defendant maintains its principal -place of business and such litigation shall be governed by laws of that -jurisdiction, without reference to its conflict-of-law provisions. -Nothing in this Section shall prevent a party's ability to bring -cross-claims or counter-claims. - -9. Miscellaneous ----------------- - -This License represents the complete agreement concerning the subject -matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent -necessary to make it enforceable. Any law or regulation which provides -that the language of a contract shall be construed against the drafter -shall not be used to construe this License against a Contributor. - -10. Versions of the License ---------------------------- - -10.1. New Versions - -Mozilla Foundation is the license steward. Except as provided in Section -10.3, no one other than the license steward has the right to modify or -publish new versions of this License. Each version will be given a -distinguishing version number. - -10.2. Effect of New Versions - -You may distribute the Covered Software under the terms of the version -of the License under which You originally received the Covered Software, -or under the terms of any subsequent version published by the license -steward. - -10.3. Modified Versions - -If you create software not governed by this License, and you want to -create a new license for such software, you may create and use a -modified version of this License if you rename the license and remove -any references to the name of the license steward (except to note that -such modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary -Licenses - -If You choose to distribute Source Code Form that is Incompatible With -Secondary Licenses under the terms of this version of the License, the -notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice -------------------------------------------- - - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular -file, then You may include the notice in a location (such as a LICENSE -file in a relevant directory) where a recipient would be likely to look -for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice ---------------------------------------------------------- - - This Source Code Form is "Incompatible With Secondary Licenses", as - defined by the Mozilla Public License, v. 2.0. diff --git a/openg2p-spar-mapper-api/README.md b/openg2p-spar-mapper-api/README.md deleted file mode 100644 index 6316c73..0000000 --- a/openg2p-spar-mapper-api/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# openg2p-spar-mapper-api -[![Pre-commit Status](https://github.com/OpenG2P/openg2p-spar-mapper-api/actions/workflows/pre-commit.yml/badge.svg?branch=develop)](https://github.com/OpenG2P/openg2p-spar-mapper-api/actions/workflows/pre-commit.yml?query=branch%3Adevelop) -[![Build Status](https://github.com/OpenG2P/openg2p-spar-mapper-api/actions/workflows/test.yml/badge.svg?branch=develop)](https://github.com/OpenG2P/openg2p-spar-mapper-api/actions/workflows/test.yml?query=branch%3Adevelop) -[![codecov](https://codecov.io/gh/OpenG2P/openg2p-spar-mapper-api/branch/develop/graph/badge.svg)](https://codecov.io/gh/OpenG2P/openg2p-spar-mapper-api) -[![openapi](https://img.shields.io/badge/open--API-swagger-brightgreen)](https://validator.swagger.io/?url=https://raw.githubusercontent.com/OpenG2P/openg2p-spar-mapper-api/develop/api-docs/generated/openapi.json) -![PyPI](https://img.shields.io/pypi/v/openg2p-spar-mapper-api?label=pypi%20package) -![PyPI - Downloads](https://img.shields.io/pypi/dm/openg2p-spar-mapper-api) - - - -## Licenses - -This repository is licensed under [MPL-2.0](LICENSE). diff --git a/openg2p-spar-mapper-api/main.py b/openg2p-spar-mapper-api/main.py deleted file mode 100755 index 3032bf3..0000000 --- a/openg2p-spar-mapper-api/main.py +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env python3 - -# ruff: noqa: I001 - -from src.app import Initializer - -main_init = Initializer() - -main_init.main() diff --git a/openg2p-spar-mapper-api/src/controllers/g2pconnect/async_mapper_controller.py b/openg2p-spar-mapper-api/src/controllers/g2pconnect/async_mapper_controller.py deleted file mode 100644 index 778de5d..0000000 --- a/openg2p-spar-mapper-api/src/controllers/g2pconnect/async_mapper_controller.py +++ /dev/null @@ -1,178 +0,0 @@ -import asyncio -import uuid -import httpx -import logging - -from openg2p_fastapi_common.controller import BaseController -from openg2p_g2pconnect_common_lib.common.schemas import ( - Request, - AsyncResponse, - AsyncCallbackRequest, -) -from openg2p_g2pconnect_common_lib.spar.schemas.link import ( - SingleLinkResponse, -) -from ...config import Settings -from ...services import ( - RequestValidation, - AsyncResponseHelper, - MapperService, - RequestValidationException, -) - - -_config = Settings.get_config() -_logger = logging.getLogger(_config.logging_default_logger_name) - - -class AsyncMapperController(BaseController): - def __init__(self, **kwargs): - super().__init__(**kwargs) - - self.mapper_service = MapperService.get_component() - - self.router.prefix += "/g2pconnect/mapper/async" - self.router.tags += ["G2PConnect Mapper Async"] - - self.action_to_method = { - "link": self.mapper_service.link, - # "update": self.update_async, - # "resolve": self.resolve_async, - # "unlink": self.unlink_async, - } - - self.router.add_api_route( - "/link", - self.link_async, - responses={200: {"model": AsyncResponse}}, - methods=["POST"], - ) - # self.router.add_api_route( - # "/update", - # self.update, - # responses={200: {"model": AsyncResponse}}, - # methods=["POST"], - # ) - # self.router.add_api_route( - # "/resolve", - # self.resolve, - # responses={200: {"model": AsyncResponse}}, - # methods=["POST"], - # ) - # self.router.add_api_route( - # "/unlink", - # self.unlink, - # responses={200: {"model": AsyncResponse}}, - # methods=["POST"], - # ) - - async def link_async(self, request: Request): - - correlation_id = str(uuid.uuid4()) - asyncio.create_task( - self.handle_service_and_callback(request, correlation_id, "link") - ) - return AsyncResponseHelper.get_component().construct_success_async_response( - request, - correlation_id, - ) - - # async def update(self, request: Request): - # if request.header.action != "update": - # raise BaseAppException( - # code="MPR-REQ-400", - # message="Received Invalid action in header for 'update'.", - # http_status_code=400, - # ) - # # TODO: For now returning random correlation id. - # correlation_id = str(uuid.uuid4()) - # # TODO: For now creating async task and forgetting - # asyncio.create_task(self.mapper_service.update(correlation_id, request)) - # - # return AsyncResponse( - # message=AsyncResponseMessage( - # ack_status=AsyncAck.ACK, - # correlation_id=correlation_id, - # timestamp=datetime.utcnow(), - # ) - # ) - # - # async def resolve(self, request: AsyncRequest): - # if request.header.action != "resolve": - # raise BaseAppException( - # code="MPR-REQ-400", - # message="Received Invalid action in header for 'resolve'.", - # http_status_code=400, - # ) - # # TODO: For now returning random correlation id. - # correlation_id = str(uuid.uuid4()) - # # TODO: For now creating async task and forgetting - # asyncio.create_task(self.mapper_service.resolve(correlation_id, request)) - # - # return AsyncResponse( - # message=AsyncResponseMessage( - # ack_status=AsyncAck.ACK, - # correlation_id=correlation_id, - # timestamp=datetime.utcnow(), - # ) - # ) - - async def unlink(self): - raise NotImplementedError() - - async def handle_service_and_callback( - self, request: Request, correlation_id: str, action: str - ): - try: - RequestValidation.validate_request(request) - RequestValidation.validate_link_request_header(request) - single_link_responses: list[SingleLinkResponse] = ( - await self.action_to_method[action](request) - ) - async_call_back_request: ( - AsyncCallbackRequest - ) = AsyncResponseHelper.get_component().construct_success_async_callback_request( - request, correlation_id, single_link_responses - ) - await self.make_callback( - async_call_back_request, - url=request.header.sender_uri, - url_suffix=f"/on-{action}", - ) - except RequestValidationException as e: - _logger.error(f"Error in handle_service_and_callback: {e}") - error_response = AsyncResponseHelper.get_component().construct_error_async_callback_request( - request, correlation_id, e - ) - await self.make_callback( - error_response, - url=request.header.sender_uri, - url_suffix=f"/on-{action}", - ) - - async def make_callback( - self, async_call_back_request: AsyncCallbackRequest, url=None, url_suffix=None - ): - if not (url or _config.default_callback_url): - return - elif not url: - url = _config.default_callback_url - - asyncio.ensure_future( - self._callback(async_call_back_request, url=url, url_suffix=url_suffix) - ) - - async def _callback( - self, async_call_back_request: AsyncCallbackRequest, url, url_suffix=None - ): - try: - res = httpx.post( - f"{url.rstrip('/')}{url_suffix}", - headers={"content-type": "application/json"}, - content=async_call_back_request.model_dump_json(), - timeout=_config.default_callback_timeout, - ) - - res.raise_for_status() - except Exception as e: - _logger.error(f"Error during callback: {e}") diff --git a/openg2p-spar-mapper-api/src/schemas/__init__.py b/openg2p-spar-mapper-api/src/schemas/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openg2p-spar-mapper-api/tests/__init__.py b/openg2p-spar-mapper-api/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/openg2p-spar-mapper-api/tox.ini b/openg2p-spar-mapper-api/tox.ini deleted file mode 100644 index fcb1d73..0000000 --- a/openg2p-spar-mapper-api/tox.ini +++ /dev/null @@ -1,17 +0,0 @@ -[tox] -env_list = - clean, - py310 -minversion = 4.11.3 - -[testenv] -description = run the tests with pytest -commands = pytest --cov tests -deps = - . - pytest-cov - -[testenv:clean] -deps = coverage -skip_install = true -commands = coverage erase diff --git a/openg2p-spar-mapper-api/pyproject.toml b/pyproject.toml similarity index 73% rename from openg2p-spar-mapper-api/pyproject.toml rename to pyproject.toml index d9c334b..5a689cf 100644 --- a/openg2p-spar-mapper-api/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "openg2p-spar-mapper-api" authors = [ { name="OpenG2P", email="info@openg2p.org" }, ] -description = "" +description = "SPAR G2P Connect ID Mapper" readme = "README.md" requires-python = ">=3.7" classifiers = [ @@ -17,15 +17,15 @@ classifiers = [ ] dependencies = [ "openg2p-fastapi-common", - "openg2p-fastapi-auth", + "openg2p-g2pconnect-common-lib", ] dynamic = ["version"] [project.urls] Homepage = "https://openg2p.org" Documentation = "https://docs.openg2p.org/" -Repository = "https://github.com/OpenG2P/openg2p-spar-mapper-api" -Source = "https://github.com/OpenG2P/openg2p-spar-mapper-api" +Repository = "https://github.com/OpenG2P/social-payments-account-registry" +Source = "https://github.com/OpenG2P/social-payments-account-registry" [tool.hatch.version] path = "src/openg2p_spar_mapper_api/__init__.py" diff --git a/openg2p-spar-mapper-api/.dockerignore b/src/openg2p_spar_mapper_api/.dockerignore similarity index 100% rename from openg2p-spar-mapper-api/.dockerignore rename to src/openg2p_spar_mapper_api/.dockerignore diff --git a/openg2p-spar-mapper-api/.editorconfig b/src/openg2p_spar_mapper_api/.editorconfig similarity index 100% rename from openg2p-spar-mapper-api/.editorconfig rename to src/openg2p_spar_mapper_api/.editorconfig diff --git a/openg2p-spar-mapper-api/.gitignore b/src/openg2p_spar_mapper_api/.gitignore similarity index 100% rename from openg2p-spar-mapper-api/.gitignore rename to src/openg2p_spar_mapper_api/.gitignore diff --git a/openg2p-spar-mapper-api/.pre-commit-config.yaml b/src/openg2p_spar_mapper_api/.pre-commit-config.yaml similarity index 100% rename from openg2p-spar-mapper-api/.pre-commit-config.yaml rename to src/openg2p_spar_mapper_api/.pre-commit-config.yaml diff --git a/openg2p-spar-mapper-api/.ruff.toml b/src/openg2p_spar_mapper_api/.ruff.toml similarity index 100% rename from openg2p-spar-mapper-api/.ruff.toml rename to src/openg2p_spar_mapper_api/.ruff.toml diff --git a/openg2p-spar-mapper-api/src/__init__.py b/src/openg2p_spar_mapper_api/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/src/__init__.py rename to src/openg2p_spar_mapper_api/__init__.py diff --git a/openg2p-spar-mapper-api/src/app.py b/src/openg2p_spar_mapper_api/app.py similarity index 99% rename from openg2p-spar-mapper-api/src/app.py rename to src/openg2p_spar_mapper_api/app.py index 625ef71..4bb8553 100644 --- a/openg2p-spar-mapper-api/src/app.py +++ b/src/openg2p_spar_mapper_api/app.py @@ -2,15 +2,11 @@ import asyncio from .config import Settings - -_config = Settings.get_config() - from openg2p_fastapi_common.app import Initializer as BaseInitializer from .controllers.g2pconnect import ( SyncMapperController, AsyncMapperController, ) - from .models import IdFaMapping from .services import ( MapperService, @@ -21,6 +17,7 @@ AsyncRequestHelper, AsyncResponseHelper, ) +_config = Settings.get_config() class Initializer(BaseInitializer): diff --git a/openg2p-spar-mapper-api/src/config.py b/src/openg2p_spar_mapper_api/config.py similarity index 99% rename from openg2p-spar-mapper-api/src/config.py rename to src/openg2p_spar_mapper_api/config.py index 14e328e..d1ef875 100644 --- a/openg2p-spar-mapper-api/src/config.py +++ b/src/openg2p_spar_mapper_api/config.py @@ -23,7 +23,7 @@ class Settings(BaseSettings): openapi_version: str = __version__ db_dbname: str = "spar-db" - + default_callback_url: Optional[AnyUrl] = None default_callback_timeout: int = 10 callback_sender_id: str = "spar.dev.openg2p.net" diff --git a/openg2p-spar-mapper-api/__init__.py b/src/openg2p_spar_mapper_api/controllers/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/__init__.py rename to src/openg2p_spar_mapper_api/controllers/__init__.py diff --git a/openg2p-spar-mapper-api/src/controllers/g2pconnect/__init__.py b/src/openg2p_spar_mapper_api/controllers/g2pconnect/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/src/controllers/g2pconnect/__init__.py rename to src/openg2p_spar_mapper_api/controllers/g2pconnect/__init__.py diff --git a/src/openg2p_spar_mapper_api/controllers/g2pconnect/async_mapper_controller.py b/src/openg2p_spar_mapper_api/controllers/g2pconnect/async_mapper_controller.py new file mode 100644 index 0000000..3ea590d --- /dev/null +++ b/src/openg2p_spar_mapper_api/controllers/g2pconnect/async_mapper_controller.py @@ -0,0 +1,170 @@ +import asyncio +import uuid +import httpx +import logging + +from openg2p_fastapi_common.controller import BaseController +from openg2p_g2pconnect_common_lib.common.schemas import ( + Request, + AsyncResponse, + AsyncCallbackRequest, +) +from openg2p_g2pconnect_common_lib.spar.schemas.link import ( + SingleLinkResponse, +) +from ...config import Settings +from ...services import ( + RequestValidation, + AsyncResponseHelper, + MapperService, + RequestValidationException, +) + + +_config = Settings.get_config() +_logger = logging.getLogger(_config.logging_default_logger_name) + + +async def _callback( + async_call_back_request: AsyncCallbackRequest, url, url_suffix=None +): + try: + res = httpx.post( + f"{url.rstrip('/')}{url_suffix}", + headers={"content-type": "application/json"}, + content=async_call_back_request.model_dump_json(), + timeout=_config.default_callback_timeout, + ) + + res.raise_for_status() + except Exception as e: + _logger.error(f"Error during callback: {e}") + + +class AsyncMapperController(BaseController): + def __init__(self, **kwargs): + super().__init__(**kwargs) + + self.mapper_service = MapperService.get_component() + + self.router.prefix += "/g2pconnect/mapper/async" + self.router.tags += ["G2PConnect Mapper Async"] + + self.action_to_method = { + "link": self.mapper_service.link, + "update": self.mapper_service.update, + "resolve": self.mapper_service.resolve, + "unlink": self.mapper_service.unlink, + } + + self.router.add_api_route( + "/link", + self.link_async, + responses={200: {"model": AsyncResponse}}, + methods=["POST"], + ) + self.router.add_api_route( + "/update", + self.update_async, + responses={200: {"model": AsyncResponse}}, + methods=["POST"], + ) + self.router.add_api_route( + "/resolve", + self.resolve_async, + responses={200: {"model": AsyncResponse}}, + methods=["POST"], + ) + self.router.add_api_route( + "/unlink", + self.unlink_async, + responses={200: {"model": AsyncResponse}}, + methods=["POST"], + ) + + async def link_async(self, request: Request): + + correlation_id = str(uuid.uuid4()) + await asyncio.create_task( + self.handle_service_and_callback(request, correlation_id, "link") + ) + return AsyncResponseHelper.get_component().construct_success_async_response( + request, + correlation_id, + ) + + async def update_async(self, request: Request): + + correlation_id = str(uuid.uuid4()) + await asyncio.create_task( + self.handle_service_and_callback(request, correlation_id, "update") + ) + return AsyncResponseHelper.get_component().construct_success_async_response( + request, + correlation_id, + ) + + async def resolve_async(self, request: Request): + + correlation_id = str(uuid.uuid4()) + await asyncio.create_task( + self.handle_service_and_callback(request, correlation_id, "resolve") + ) + return AsyncResponseHelper.get_component().construct_success_async_response( + request, + correlation_id, + ) + + async def unlink_async(self, request: Request): + + correlation_id = str(uuid.uuid4()) + await asyncio.create_task( + self.handle_service_and_callback(request, correlation_id, "unlink") + ) + return AsyncResponseHelper.get_component().construct_success_async_response( + request, + correlation_id, + ) + + async def handle_service_and_callback( + self, request: Request, correlation_id: str, action: str + ): + try: + RequestValidation.validate_request(request) + RequestValidation.validate_link_request_header(request) + single_link_responses: list[SingleLinkResponse] = ( + await self.action_to_method[action](request) + ) + async_call_back_request: ( + AsyncCallbackRequest + ) = AsyncResponseHelper.get_component().construct_success_async_callback_request( + request, correlation_id, single_link_responses + ) + await self.make_callback( + async_call_back_request, + url=request.header.sender_uri, + url_suffix=f"/on-{action}", + ) + except RequestValidationException as e: + _logger.error(f"Error in handle_service_and_callback: {e}") + error_response = AsyncResponseHelper.get_component().construct_error_async_callback_request( + request, correlation_id, e + ) + await self.make_callback( + error_response, + url=request.header.sender_uri, + url_suffix=f"/on-{action}", + ) + + @staticmethod + async def make_callback( + async_call_back_request: AsyncCallbackRequest, url=None, url_suffix=None + ): + if not (url or _config.default_callback_url): + return + elif not url: + url = _config.default_callback_url + + asyncio.ensure_future( + _callback(async_call_back_request, url=url, url_suffix=url_suffix) + ) diff --git a/openg2p-spar-mapper-api/src/controllers/g2pconnect/sync_mapper_controller.py b/src/openg2p_spar_mapper_api/controllers/g2pconnect/sync_mapper_controller.py similarity index 81% rename from openg2p-spar-mapper-api/src/controllers/g2pconnect/sync_mapper_controller.py rename to src/openg2p_spar_mapper_api/controllers/g2pconnect/sync_mapper_controller.py index 3ef7b0c..001079b 100644 --- a/openg2p-spar-mapper-api/src/controllers/g2pconnect/sync_mapper_controller.py +++ b/src/openg2p_spar_mapper_api/controllers/g2pconnect/sync_mapper_controller.py @@ -80,7 +80,7 @@ async def update_sync(self, request: Request): return error_response single_update_responses: list[SingleUpdateResponse] = ( - await self.mapper_service.link(request) + await self.mapper_service.update(request) ) return SyncResponseHelper.get_component().construct_success_sync_update_response( request, @@ -98,12 +98,27 @@ async def resolve_sync(self, request: Request): return error_response single_resolve_responses: list[SingleResolveResponse] = ( - await self.mapper_service.link(request) + await self.mapper_service.resolve(request) ) return SyncResponseHelper.get_component().construct_success_sync_resolve_response( request, single_resolve_responses, ) - async def unlink_sync(self): - raise NotImplementedError() + async def unlink_sync(self, request: Request): + try: + RequestValidation.validate_request(request) + RequestValidation.validate_unlink_request_header(request) + except RequestValidationException as e: + error_response = SyncResponseHelper.get_component().construct_error_sync_response( + request, e + ) + return error_response + + single_unlink_responses: list[SingleResolveResponse] = ( + await self.mapper_service.unlink(request) + ) + return SyncResponseHelper.get_component().construct_success_sync_unlink_response( + request, + single_unlink_responses, + ) diff --git a/openg2p-spar-mapper-api/src/controllers/__init__.py b/src/openg2p_spar_mapper_api/errors/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/src/controllers/__init__.py rename to src/openg2p_spar_mapper_api/errors/__init__.py diff --git a/openg2p-spar-mapper-api/src/errors/error_codes.py b/src/openg2p_spar_mapper_api/errors/error_codes.py similarity index 100% rename from openg2p-spar-mapper-api/src/errors/error_codes.py rename to src/openg2p_spar_mapper_api/errors/error_codes.py diff --git a/openg2p-spar-mapper-api/src/models/__init__.py b/src/openg2p_spar_mapper_api/models/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/src/models/__init__.py rename to src/openg2p_spar_mapper_api/models/__init__.py diff --git a/openg2p-spar-mapper-api/src/models/id_fa_mapping.py b/src/openg2p_spar_mapper_api/models/id_fa_mapping.py similarity index 100% rename from openg2p-spar-mapper-api/src/models/id_fa_mapping.py rename to src/openg2p_spar_mapper_api/models/id_fa_mapping.py diff --git a/openg2p-spar-mapper-api/src/errors/__init__.py b/src/openg2p_spar_mapper_api/schemas/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/src/errors/__init__.py rename to src/openg2p_spar_mapper_api/schemas/__init__.py diff --git a/openg2p-spar-mapper-api/src/services/__init__.py b/src/openg2p_spar_mapper_api/services/__init__.py similarity index 100% rename from openg2p-spar-mapper-api/src/services/__init__.py rename to src/openg2p_spar_mapper_api/services/__init__.py diff --git a/openg2p-spar-mapper-api/src/services/exceptions.py b/src/openg2p_spar_mapper_api/services/exceptions.py similarity index 76% rename from openg2p-spar-mapper-api/src/services/exceptions.py rename to src/openg2p_spar_mapper_api/services/exceptions.py index 9a2aa5b..448adf7 100644 --- a/openg2p-spar-mapper-api/src/services/exceptions.py +++ b/src/openg2p_spar_mapper_api/services/exceptions.py @@ -1,7 +1,6 @@ from openg2p_g2pconnect_common_lib.common.schemas import StatusEnum -from openg2p_g2pconnect_common_lib.spar.schemas import LinkStatusReasonCode -from openg2p_g2pconnect_common_lib.spar.schemas.resolve import ResolveStatusReasonCode -from openg2p_g2pconnect_common_lib.spar.schemas.update import UpdateStatusReasonCode +from openg2p_g2pconnect_common_lib.spar.schemas import LinkStatusReasonCode,ResolveStatusReasonCode,UpdateStatusReasonCode,UnlinkStatusReasonCode + class LinkValidationException(Exception): @@ -28,6 +27,13 @@ def __init__(self, message, status, validation_error_type: ResolveStatusReasonCo self.validation_error_type: ResolveStatusReasonCode = validation_error_type +class UnlinkValidationException(Exception): + def __init__(self, message, status, validation_error_type: UnlinkStatusReasonCode): + self.message = message + super().__init__(self.message) + self.status: StatusEnum = status + self.validation_error_type: UnlinkStatusReasonCode = validation_error_type + class RequestValidationException(Exception): # TODO : Add code def __init__(self, code, message): diff --git a/openg2p-spar-mapper-api/src/services/id_fa_mapping_validations.py b/src/openg2p_spar_mapper_api/services/id_fa_mapping_validations.py similarity index 76% rename from openg2p-spar-mapper-api/src/services/id_fa_mapping_validations.py rename to src/openg2p_spar_mapper_api/services/id_fa_mapping_validations.py index f4646b8..bdba084 100644 --- a/openg2p-spar-mapper-api/src/services/id_fa_mapping_validations.py +++ b/src/openg2p_spar_mapper_api/services/id_fa_mapping_validations.py @@ -8,14 +8,19 @@ LinkStatusReasonCode, SingleUpdateRequest, UpdateStatusReasonCode, + UnlinkStatusReasonCode, + SingleResolveRequest, + SingleUnlinkRequest, + SingleLinkRequest, + ResolveStatusReasonCode, ) -from openg2p_g2pconnect_common_lib.spar.schemas import SingleLinkRequest from sqlalchemy import and_, select from .exceptions import ( LinkValidationException, UpdateValidationException, ResolveValidationException, + UnlinkValidationException ) from ..models import IdFaMapping @@ -134,3 +139,37 @@ async def validate_resolve_request( validation_error_type=ResolveStatusReasonCode.rjct_reference_id_duplicate, ) return None + + @staticmethod + async def validate_unlink_request( + connection, single_unlink_request: SingleUnlinkRequest + ) -> None: + + if not single_unlink_request.id: + raise UnlinkValidationException( + message="ID is null", + validation_error_type=UnlinkValidationException.rjct_id_invalid, + ) + + if not single_unlink_request.fa: + raise UnlinkValidationException( + message="FA is null", + validation_error_type=UnlinkValidationException.rjct_fa_invalid, + ) + result = await connection.execute( + select(IdFaMapping).where( + and_( + IdFaMapping.id_value == single_unlink_request.id, + IdFaMapping.fa_value == single_unlink_request.fa, + ) + ) + ) + link_request_from_db = result.first() + + if link_request_from_db is None: + raise UnlinkValidationException( + message="ID doesnt exist please link first", + validation_error_type=UnlinkStatusReasonCode.rjct_reference_id_duplicate, + ) + + return None \ No newline at end of file diff --git a/openg2p-spar-mapper-api/src/services/mapper.py b/src/openg2p_spar_mapper_api/services/mapper.py similarity index 82% rename from openg2p-spar-mapper-api/src/services/mapper.py rename to src/openg2p_spar_mapper_api/services/mapper.py index fbefc45..4cb7668 100644 --- a/openg2p-spar-mapper-api/src/services/mapper.py +++ b/src/openg2p_spar_mapper_api/services/mapper.py @@ -18,6 +18,9 @@ ResolveRequest, LinkStatusReasonCode, UpdateStatusReasonCode, + UnlinkRequest, + SingleUnlinkResponse, + UnlinkStatusReasonCode ) from sqlalchemy.ext.asyncio import async_sessionmaker from sqlalchemy import and_, select @@ -25,6 +28,7 @@ from ..models import IdFaMapping from ..services.exceptions import ( LinkValidationException, + UnlinkValidationException, UpdateValidationException, ResolveValidationException, ) @@ -363,3 +367,75 @@ def construct_single_resolve_response_for_failure(single_resolve_request, error) additional_info=None, locale=single_resolve_request.locale, ) + + async def unlink(self, request: Request): + session_maker = async_sessionmaker(dbengine.get(), expire_on_commit=False) + async with session_maker() as session: + unlinkRequest: UnlinkRequest = UnlinkRequest.model_validate(request.message) + single_unlink_responses: list[SingleUnlinkResponse] = [] + mappings_to_delete = [] + for single_unlink_request in unlinkRequest.link_request: + + try: + await IdFaMappingValidations.get_component().validate_unlink_request( + connection=session, single_update_request=single_unlink_request + ) + mappings_to_delete.append( + self.unlink_id_fa_mapping(single_unlink_request) + ) + single_unlink_responses.append( + self.construct_single_unlink_response_for_success( + single_unlink_request + ) + ) + except UnlinkValidationException as e: + custom_exception = UnlinkValidationException( + message=" ID doesn't exist", + status=StatusEnum.rjct, + validation_error_type=UnlinkStatusReasonCode.rjct_id_invalid, + ) + single_unlink_responses.append( + self.construct_single_unlink_response_for_failure( + single_unlink_request, e + ) + ) + session.delete(*mappings_to_delete) + await session.commit() + return single_unlink_responses + + @staticmethod + def unlink_id_fa_mapping(single_unlink_request): + return IdFaMapping( + id_value=single_unlink_request.id, + fa_value=single_unlink_request.fa, + name=single_unlink_request.name, + phone=single_unlink_request.phone_number, + additional_info=single_unlink_request.additional_info, + active=True, + ) + @staticmethod + def construct_single_unlink_response_for_success(single_unlink_request): + return SingleUnlinkResponse( + reference_id=single_unlink_request.reference_id, + timestamp=datetime.now(), + fa=single_unlink_request.fa, + status=StatusEnum.succ, + status_reason_code=None, + status_reason_message=None, + additional_info=None, + locale=single_unlink_request.locale, + ) + + @staticmethod + def construct_single_unlink_response_for_failure(single_unlink_request, error): + + return SingleUnlinkResponse( + reference_id=single_unlink_request.reference_id, + timestamp=datetime.now(), + fa=single_unlink_request.fa, + status=StatusEnum.rjct, + status_reason_code=error.validation_error_type, + status_reason_message=error.message, + additional_info=None, + locale=single_unlink_request.locale, + ) \ No newline at end of file diff --git a/openg2p-spar-mapper-api/src/services/request_helper.py b/src/openg2p_spar_mapper_api/services/request_helper.py similarity index 100% rename from openg2p-spar-mapper-api/src/services/request_helper.py rename to src/openg2p_spar_mapper_api/services/request_helper.py diff --git a/openg2p-spar-mapper-api/src/services/request_validations.py b/src/openg2p_spar_mapper_api/services/request_validations.py similarity index 79% rename from openg2p-spar-mapper-api/src/services/request_validations.py rename to src/openg2p_spar_mapper_api/services/request_validations.py index 29aab25..f0c7127 100644 --- a/openg2p-spar-mapper-api/src/services/request_validations.py +++ b/src/openg2p_spar_mapper_api/services/request_validations.py @@ -35,6 +35,14 @@ def validate_resolve_request_header(request) -> None: return None @staticmethod + def validate_unlink_request_header(request) -> None: + # if request.header.action != "unlink": + # raise RequestValidationException( + # code=SyncUnlinkStatusReasonCodeEnum.rjct_action_not_supported, + # message=SyncUnlinkStatusReasonCodeEnum.rjct_action_not_supported, + # ) + return None + @staticmethod def validate_request(request) -> None: # TODO: Validate the request return None diff --git a/openg2p-spar-mapper-api/src/services/response_helper.py b/src/openg2p_spar_mapper_api/services/response_helper.py similarity index 83% rename from openg2p-spar-mapper-api/src/services/response_helper.py rename to src/openg2p_spar_mapper_api/services/response_helper.py index 47e904c..dc18fa2 100644 --- a/openg2p-spar-mapper-api/src/services/response_helper.py +++ b/src/openg2p_spar_mapper_api/services/response_helper.py @@ -25,6 +25,10 @@ SingleResolveResponse, ResolveResponse, ResolveStatusReasonCode, + UnlinkRequest, + UnlinkResponse, + SingleUnlinkResponse, + UnlinkStatusReasonCode, ) from .exceptions import ( @@ -32,6 +36,7 @@ LinkValidationException, UpdateValidationException, ResolveValidationException, + UnlinkValidationException ) @@ -84,7 +89,6 @@ def construct_success_sync_link_response( @staticmethod def construct_success_sync_update_response( - self, request: Request, single_update_responses: list[SingleUpdateResponse], ) -> SyncResponse: @@ -129,7 +133,6 @@ def construct_success_sync_update_response( @staticmethod def construct_success_sync_resolve_response( - self, request: Request, single_resolve_responses: list[SingleResolveResponse], ) -> SyncResponse: @@ -172,9 +175,53 @@ def construct_success_sync_resolve_response( message=resolveResponse, ) + @staticmethod + def construct_success_sync_unlink_response( + request: Request, + single_unlink_responses: list[SingleUnlinkResponse], + ) -> SyncResponse: + unlinkRequest: UnlinkRequest = UnlinkRequest.model_validate(request.message) + unlinkResponse: UnlinkResponse = UnlinkResponse( + transaction_id=unlinkRequest.transaction_id, + correlation_id=None, + link_response=single_unlink_responses, + ) + total_count = len(unlinkResponse.link_response) + completed_count = len( + [ + link + for link in unlinkResponse.link_response + if link.status == StatusEnum.succ + ] + ) + if completed_count == 0: + raise UnlinkValidationException( + message="All requests in transaction failed.", + status=StatusEnum.rjct, + validation_error_type=ResolveStatusReasonCode.rjct_errors_too_many, + ) + return SyncResponse( + header=SyncResponseHeader( + version="1.0.0", + message_id=request.header.message_id, + message_ts=datetime.now().isoformat(), + action=request.header.action, + status=StatusEnum.succ, + status_reason_code=None, + status_reason_message=None, + total_count=total_count, + completed_count=completed_count, + sender_id=request.header.sender_id, + receiver_id=request.header.receiver_id, + is_msg_encrypted=False, + meta={}, + ), + message=unlinkResponse, + ) + @staticmethod def construct_error_sync_response( - self, request: Request, exception: RequestValidationException + request: Request, exception: RequestValidationException ) -> SyncResponse: return SyncResponse( signature=None,