From 2568d1f69bcb61186a7a7d9171cd80dba8cf557f Mon Sep 17 00:00:00 2001
From: Muhammad Rafay Nadeem
<113093783+mrafnadeem-apimatic@users.noreply.github.com>
Date: Wed, 12 Feb 2025 18:24:53 +0500
Subject: [PATCH 1/4] docs: adds contributing guide and issue templates (#32)
---
.github/ISSUE_TEMPLATE/bug-report.md | 33 ---------
.github/ISSUE_TEMPLATE/bug-report.yml | 79 ++++++++++++++++++++++
.github/ISSUE_TEMPLATE/feature-request.yml | 54 +++++++++++++++
.github/workflows/pylint.yml | 2 +-
CONTRIBUTING.md | 60 ++++++++++++++++
5 files changed, 194 insertions(+), 34 deletions(-)
delete mode 100644 .github/ISSUE_TEMPLATE/bug-report.md
create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml
create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml
create mode 100644 CONTRIBUTING.md
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
deleted file mode 100644
index 533cbe1..0000000
--- a/.github/ISSUE_TEMPLATE/bug-report.md
+++ /dev/null
@@ -1,33 +0,0 @@
----
-name: Bug Report
-about: Create a report to help us improve the library
-title: ''
-labels: bug, triage
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**To Reproduce**
-Steps to reproduce the bug:
-1. (step 1)
-2. (step 2)
-3. (step 3)
-4. ...
-
-**Screenshots**
-If applicable, add screenshots to help explain the bug.
-
-**Library version number**
-For example: 0.1.1
-
-**Environmental details**
-For example:
-OS: Linux
-Runtime version: Python 3.7
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
new file mode 100644
index 0000000..1748b38
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -0,0 +1,79 @@
+name: 🐞 Bug
+description: Report a bug or an issue you've found
+title: "[Bug]
"
+labels: ["bug"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+ - type: checkboxes
+ attributes:
+ label: Is this a new bug?
+ description: >
+ In other words: Is this an error, flaw, failure or fault? Please search issues to see if someone has already reported the bug you encountered.
+ options:
+ - label: I believe this is a new bug
+ required: true
+ - label: I have searched the existing issues, and I could not find an existing issue for this bug
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Current Behavior
+ description: A concise description of what you're experiencing.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Steps To Reproduce
+ description: Steps to reproduce the behavior.
+ placeholder: |
+ 1. In this environment...
+ 2. With this config...
+ 3. Run '...'
+ 4. See error...
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Environment
+ description: |
+ examples:
+ - **OS**: Ubuntu 20.04
+ - **Language version**: Python 3.10.11 (`python --version`)
+ - **SDK Name**: PayPal (If you are using this library as a dependency, please name the parent SDK)
+ value: |
+ - **OS**:
+ - **Language version**:
+ - **SDK Name**:
+ render: markdown
+ validations:
+ required: true
+
+ - type: textarea
+ id: logs
+ attributes:
+ label: Relevant log output
+ description: |
+ If applicable, log output to help explain your problem.
+ render: shell
+ validations:
+ required: false
+
+ - type: textarea
+ attributes:
+ label: Additional Context
+ description: |
+ Links? References? Anything that will give us more context about the issue you are encountering!
+ validations:
+ required: false
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
new file mode 100644
index 0000000..c5dd8ce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -0,0 +1,54 @@
+name: ✨ Feature
+description: Propose an extension
+title: "[Feature] "
+labels: ["enhancement"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this feature request!
+ - type: checkboxes
+ attributes:
+ label: Is this your first time submitting a feature request?
+ description: >
+ We want to make sure that features are distinct and discoverable,
+ so that other members of the community can find them and offer their thoughts.
+
+ Issues are the right place to request extensions of existing functionality.
+ options:
+ - label: I have searched the existing issues, and I could not find an existing issue for this feature
+ required: true
+ - label: I am requesting an extension of the existing functionality
+ - type: textarea
+ attributes:
+ label: Describe the feature
+ description: A clear and concise description of what you want to happen.
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Describe alternatives you've considered
+ description: |
+ A clear and concise description of any alternative solutions or features you've considered.
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Who will this benefit?
+ description: |
+ What kind of use case will this feature be useful for? Please be specific and provide examples, this will help us prioritize properly.
+ validations:
+ required: false
+ - type: input
+ attributes:
+ label: Are you interested in contributing this feature?
+ description: Let us know if you want to write some code, and how we can help.
+ validations:
+ required: false
+ - type: textarea
+ attributes:
+ label: Anything else?
+ description: |
+ Links? References? Anything that will give us more context about the feature you are suggesting!
+ validations:
+ required: false
\ No newline at end of file
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index 5dca571..8ccbc2f 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- os: [ubuntu-latest]
+ os: [ubuntu-22.04]
python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v3
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..5e98cab
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,60 @@
+# Contributing to apimatic/core-interfaces-python
+
+Thank you for your interest in contributing! 🎉 Your contributions help make this project better. We value open-source contributions to this library, please take a few minutes to review this guide before you start.
+
+---
+
+## 💡 How to Contribute
+
+### 📌 1. Reporting Issues
+
+- Search existing issues before opening a new one.
+- Use a **descriptive title** and provide clear steps to reproduce.
+- Include relevant logs, screenshots, or error messages.
+
+### 🔧 2. Making Changes
+
+#### Step 1: Create a Branch
+
+- Create a new branch from `main`:
+ ```sh
+ git checkout -b your-feature-name
+ ```
+
+#### Step 2: Make Your Changes
+
+- Follow the project's **coding standards**.
+- Add **unit tests** if applicable.
+- Ensure your changes **do not break existing functionality**.
+
+#### Step 3: Commit Changes
+
+- Use clear and descriptive commit messages:
+ ```sh
+ git commit -m "feat: Add feature description"
+ ```
+
+#### Step 4: Push & Open a PR
+
+- Push your branch:
+ ```sh
+ git push origin your-feature-name
+ ```
+- Open a **Pull Request (PR)** on GitHub:
+ - Provide a clear **description** of the changes.
+ - Mention related **issue numbers**.
+ - Request a **review** from maintainers.
+ - Make sure your changes clear all the **PR Checks**.
+
+---
+
+## 📜 License
+
+By contributing, you agree that your contributions will be licensed under the project's [LICENSE](LICENSE).
+
+---
+
+💬 **Questions?**
+If you need help, feel free to open issues, we will be happy to help.
+
+Happy Coding! 🚀
From 5831770b2cca094c813eff42297d9c9b47087cc8 Mon Sep 17 00:00:00 2001
From: MuHamza30
Date: Mon, 7 Jul 2025 11:23:14 +0500
Subject: [PATCH 2/4] ci(dependabot): slack alert (#34)
---
.../workflows/dependabot-notifications.yml | 126 ++++++++++++++++++
1 file changed, 126 insertions(+)
create mode 100644 .github/workflows/dependabot-notifications.yml
diff --git a/.github/workflows/dependabot-notifications.yml b/.github/workflows/dependabot-notifications.yml
new file mode 100644
index 0000000..e16a921
--- /dev/null
+++ b/.github/workflows/dependabot-notifications.yml
@@ -0,0 +1,126 @@
+name: Dependabot Notifications
+
+on:
+ workflow_run:
+ workflows: ["*"]
+ types:
+ - completed
+
+jobs:
+ notify-checks:
+ runs-on: ubuntu-latest
+ if: github.actor == 'dependabot[bot]'
+ steps:
+ - name: Get PR Information
+ if: github.actor == 'dependabot[bot]'
+ id: get-pr-info
+ uses: actions/github-script@v6
+ with:
+ script: |
+ const { owner, repo } = context.repo;
+ const run = context.payload.workflow_run;
+
+ // Get PR directly from the workflow run's head SHA
+ const response = await github.rest.repos.listPullRequestsAssociatedWithCommit({
+ owner,
+ repo,
+ commit_sha: run.head_sha
+ });
+
+ const pr = response.data[0]; // Get the first associated PR
+
+ if (pr) {
+ core.exportVariable('PR_TITLE', pr.title);
+ core.exportVariable('PR_AUTHOR', pr.user.login);
+ core.exportVariable('PR_LINK', pr.html_url);
+ core.exportVariable('PR_NUMBER', pr.number.toString());
+ } else {
+ core.exportVariable('PR_TITLE', 'Unknown');
+ core.exportVariable('PR_AUTHOR', context.actor);
+ core.exportVariable('PR_LINK', `https://github.com/${owner}/${repo}/pulls`);
+ core.exportVariable('PR_NUMBER', '');
+ }
+
+ // Get check runs for this commit
+ const checkRuns = await github.rest.checks.listForRef({
+ owner,
+ repo,
+ ref: run.head_sha
+ });
+
+ // Count different check conclusions
+ const stats = checkRuns.data.check_runs.reduce((acc, check) => {
+ acc[check.conclusion] = (acc[check.conclusion] || 0) + 1;
+ return acc;
+ }, {});
+
+ // Create status summary
+ const summary = Object.entries(stats)
+ .map(([status, count]) => `${count} ${status}`)
+ .join(', ');
+
+ core.exportVariable('CHECKS_SUMMARY', summary);
+
+ // Determine overall status
+ const hasFailures = stats.failure > 0;
+ const hasSuccess = stats.success > 0;
+ const hasCancelled = stats.cancelled > 0;
+
+ let overallStatus;
+ if (hasFailures) {
+ overallStatus = 'failure';
+ } else if (hasCancelled && !hasSuccess) {
+ overallStatus = 'cancelled';
+ } else if (hasSuccess) {
+ overallStatus = 'success';
+ } else {
+ overallStatus = 'unknown';
+ }
+
+ // Only set status if this is the last workflow to complete
+ const incompleteRuns = await github.rest.actions.listWorkflowRunsForRepo({
+ owner,
+ repo,
+ head_sha: run.head_sha,
+ status: 'in_progress'
+ });
+
+ if (incompleteRuns.data.total_count === 0) {
+ core.exportVariable('ALL_CHECKS_STATUS', overallStatus);
+ core.exportVariable('SHOULD_NOTIFY', 'true');
+
+ // If checks failed and PR exists, close it
+ if ((overallStatus === 'failure' || overallStatus === 'cancelled') && pr) {
+ await github.rest.pulls.update({
+ owner,
+ repo,
+ pull_number: pr.number,
+ state: 'closed'
+ });
+
+ // Add comment explaining why PR was closed
+ await github.rest.issues.createComment({
+ owner,
+ repo,
+ issue_number: pr.number,
+ body: `This PR was automatically closed because some checks failed.\nStatus Summary: ${summary}`
+ });
+ }
+ } else {
+ core.exportVariable('SHOULD_NOTIFY', 'false');
+ }
+
+ - name: Send Slack Notification for Success
+ if: env.SHOULD_NOTIFY == 'true' && env.ALL_CHECKS_STATUS == 'success' && github.actor == 'dependabot[bot]'
+ id: slack
+ uses: slackapi/slack-github-action@v1.25.0
+ with:
+ channel-id: 'C08TLGVQ6V8'
+ slack-message: |
+ Repository: ${{ github.repository }}
+ Title: ${{ env.PR_TITLE }}
+ Author: ${{ env.PR_AUTHOR }}
+ Link: ${{ env.PR_LINK }}
+ Status Summary: ${{ env.CHECKS_SUMMARY }}
+ env:
+ SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
From eeb78166bab5ee4c0fa6785c4e1646680b030608 Mon Sep 17 00:00:00 2001
From: Ayesha <88117894+Ayeshas09@users.noreply.github.com>
Date: Fri, 1 Aug 2025 09:10:34 +0500
Subject: [PATCH 3/4] ci: add SonarQube Scan to CI workflow (#35)
---
.github/workflows/pylint.yml | 6 ++++++
README.md | 6 ++++++
sonar-project.properties | 7 +++++++
3 files changed, 19 insertions(+)
create mode 100644 sonar-project.properties
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index 8ccbc2f..22d50ac 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -38,3 +38,9 @@ jobs:
do
pylint --disable=R,C,W "$file" --fail-under=10;
done
+ - name: SonarQube Scan
+ if: ${{ matrix.python == '3.13' && github.actor != 'dependabot[bot]' }}
+ uses: SonarSource/sonarqube-scan-action@v5.2.0
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/README.md b/README.md
index 39b2024..ecd845c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# apimatic-core-interfaces
[![PyPI][pypi-version]](https://pypi.org/project/apimatic-core-interfaces/)
+[![Maintainability Rating][maintainability-badge]][maintainability-url]
+[![Vulnerabilities][vulnerabilities-badge]][vulnerabilities-url]
[![Licence][license-badge]][license-url]
## Introduction
@@ -32,3 +34,7 @@ pip install apimatic-core-interfaces
[pypi-version]: https://img.shields.io/pypi/v/apimatic-core-interfaces
[license-badge]: https://img.shields.io/badge/licence-MIT-blue
[license-url]: LICENSE
+[maintainability-badge]: https://sonarcloud.io/api/project_badges/measure?project=apimatic_core-interfaces-python&metric=sqale_rating
+[maintainability-url]: https://sonarcloud.io/summary/new_code?id=apimatic_core-interfaces-python
+[vulnerabilities-badge]: https://sonarcloud.io/api/project_badges/measure?project=apimatic_core-interfaces-python&metric=vulnerabilities
+[vulnerabilities-url]: https://sonarcloud.io/summary/new_code?id=apimatic_core-interfaces-python
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000..096b3e6
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,7 @@
+sonar.projectKey=apimatic_core-interfaces-python
+sonar.projectName=APIMatic Core Interfaces Python
+sonar.organization=apimatic
+sonar.host.url=https://sonarcloud.io
+sonar.sourceEncoding=UTF-8
+
+sonar.sources=apimatic_core_interfaces
\ No newline at end of file
From f553361119b1c40561ba5f4a554cb92cb90919fd Mon Sep 17 00:00:00 2001
From: Muhammad Sufyan
Date: Fri, 3 Oct 2025 17:48:48 +0500
Subject: [PATCH 4/4] feat(webhooks & callbacks): add contracts for webhooks
and callbacks (#37)
This commit adds comprehensive contracts for webhook and callback handling by introducing a signature verification framework and HTTP request modeling. The changes enable language-agnostic webhook implementations across core libraries and SDKs.
- Added signature verification interface with standardized result handling
- Introduced framework-agnostic HTTP request model for webhook processing
- Updated package structure and documentation to reflect new capabilities
---
.gitignore | 2 +
README.md | 57 +++++++++++--------
apimatic_core_interfaces/__init__.py | 6 +-
apimatic_core_interfaces/http/__init__.py | 3 +
apimatic_core_interfaces/http/request.py | 14 +++++
apimatic_core_interfaces/security/__init__.py | 3 +
.../security/signature_verifier.py | 29 ++++++++++
apimatic_core_interfaces/types/__init__.py | 3 +-
.../types/signature_verification_result.py | 26 +++++++++
setup.py | 2 +-
sonar-project.properties | 5 +-
11 files changed, 120 insertions(+), 30 deletions(-)
create mode 100644 apimatic_core_interfaces/http/__init__.py
create mode 100644 apimatic_core_interfaces/http/request.py
create mode 100644 apimatic_core_interfaces/security/__init__.py
create mode 100644 apimatic_core_interfaces/security/signature_verifier.py
create mode 100644 apimatic_core_interfaces/types/signature_verification_result.py
diff --git a/.gitignore b/.gitignore
index 2dc53ca..c05f410 100644
--- a/.gitignore
+++ b/.gitignore
@@ -158,3 +158,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
+*.py~
+*~
diff --git a/README.md b/README.md
index ecd845c..73debf8 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,47 @@
# apimatic-core-interfaces
-[![PyPI][pypi-version]](https://pypi.org/project/apimatic-core-interfaces/)
-[![Maintainability Rating][maintainability-badge]][maintainability-url]
-[![Vulnerabilities][vulnerabilities-badge]][vulnerabilities-url]
+[![PyPI][pypi-version]](https://pypi.org/project/apimatic-core-interfaces/)
+[![Maintainability Rating][maintainability-badge]][maintainability-url]
+[![Vulnerabilities][vulnerabilities-badge]][vulnerabilities-url]
[![Licence][license-badge]][license-url]
## Introduction
-This project contains the abstract layer for APIMatic's core library. The purpose of creating interfaces is to separate out the functionalities needed by APIMatic's core library module. The goal is to support scalability and feature enhancement of the core library and the SDKs along with avoiding any breaking changes by reducing tight coupling between modules through the introduction of interfaces.
+This project contains the abstract layer for APIMatic's core library. The purpose of creating interfaces is to separate out the functionalities needed by APIMatic's core library module. The goal is to support scalability and feature enhancement of the core library and the SDKs, while avoiding breaking changes by reducing tight coupling between modules.
-## Version supported
-Currenty APIMatic supports `Python version 3.7+` hence the apimatic-core-interfaces will need the same versions to be supported.
+## Version Supported
+Currently, APIMatic supports **Python version 3.7+**, hence the `apimatic-core-interfaces` package requires the same version support.
## Installation
-Simply run the command below in your SDK as the apimatic-core-interfaces will be added as a dependency in the SDK.
-```python
+Run the following command in your SDK (the `apimatic-core-interfaces` package will be added as a dependency):
+```bash
pip install apimatic-core-interfaces
```
## Interfaces
-| Name | Description |
-|-----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|
-| [`HttpClient`](apimatic_core_interfaces/client/http_client.py) | To save both Request and Response after the completion of response |
-| [`ResponseFactory`](apimatic_core_interfaces/factories/response_factory.py) | To convert the client-adapter response into a custom HTTP response |
-| [`Authentication`](apimatic_core_interfaces/types/authentication.py) | To setup methods for the validation and application of the required authentication scheme |
-| [`UnionType`](apimatic_core_interfaces/types/union_type.py) | To setup methods for the validation and deserialization of OneOf/AnyOf union types |
-| [`Logger`](apimatic_core_interfaces/logger/logger.py) | An interface for the generic logger facade |
-| [`ApiLogger`](apimatic_core_interfaces/logger/api_logger.py) | An interface for logging API requests and responses |
+| Name | Description |
+|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
+| [`HttpClient`](apimatic_core_interfaces/client/http_client.py) | Saves both request and response after the completion of response. |
+| [`ResponseFactory`](apimatic_core_interfaces/factories/response_factory.py) | Converts the client-adapter response into a custom HTTP response. |
+| [`Authentication`](apimatic_core_interfaces/types/authentication.py) | Sets up methods for the validation and application of the required authentication scheme. |
+| [`UnionType`](apimatic_core_interfaces/types/union_type.py) | Sets up methods for the validation and deserialization of OneOf/AnyOf union types. |
+| [`Logger`](apimatic_core_interfaces/logger/logger.py) | An interface for the generic logger facade. |
+| [`ApiLogger`](apimatic_core_interfaces/logger/api_logger.py) | An interface for logging API requests and responses. |
+| [`SignatureVerifier`](apimatic_core_interfaces/security/signature_verifier.py) | Defines the contract for verifying the authenticity of incoming events or webhook requests. |
+
+## Types
+| Name | Description |
+|--------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
+| [`Request`](apimatic_core_interfaces/http/request.py) | Framework-agnostic request model capturing headers, method, path, body, and raw bytes. |
+| [`SignatureVerificationResult`](apimatic_core_interfaces/types/signature_verification_result.py) | Provides a structured result of the verification process, including success, failure, and error details. |
## Enumerations
-| Name | Description |
-|-------------------------------------------------------------------------------|-----------------------------------------------------------------|
-| [`HttpMethodEnum`](apimatic_core_interfaces/types/http_method_enum.py ) | Enumeration containig HTTP Methods (GET, POST, PATCH, DELETE) |
+| Name | Description |
+|----------------------------------------------------------------------------------------|---------------------------------------------------------------|
+| [`HttpMethodEnum`](apimatic_core_interfaces/types/http_method_enum.py) | Enumeration containing HTTP methods (GET, POST, PATCH, DELETE).|
-[pypi-version]: https://img.shields.io/pypi/v/apimatic-core-interfaces
-[license-badge]: https://img.shields.io/badge/licence-MIT-blue
-[license-url]: LICENSE
-[maintainability-badge]: https://sonarcloud.io/api/project_badges/measure?project=apimatic_core-interfaces-python&metric=sqale_rating
-[maintainability-url]: https://sonarcloud.io/summary/new_code?id=apimatic_core-interfaces-python
-[vulnerabilities-badge]: https://sonarcloud.io/api/project_badges/measure?project=apimatic_core-interfaces-python&metric=vulnerabilities
+[pypi-version]: https://img.shields.io/pypi/v/apimatic-core-interfaces
+[license-badge]: https://img.shields.io/badge/licence-MIT-blue
+[license-url]: LICENSE
+[maintainability-badge]: https://sonarcloud.io/api/project_badges/measure?project=apimatic_core-interfaces-python&metric=sqale_rating
+[maintainability-url]: https://sonarcloud.io/summary/new_code?id=apimatic_core-interfaces-python
+[vulnerabilities-badge]: https://sonarcloud.io/api/project_badges/measure?project=apimatic_core-interfaces-python&metric=vulnerabilities
[vulnerabilities-url]: https://sonarcloud.io/summary/new_code?id=apimatic_core-interfaces-python
diff --git a/apimatic_core_interfaces/__init__.py b/apimatic_core_interfaces/__init__.py
index 94e6598..500bdf4 100644
--- a/apimatic_core_interfaces/__init__.py
+++ b/apimatic_core_interfaces/__init__.py
@@ -2,5 +2,7 @@
'client',
'factories',
'types',
- 'logger'
-]
\ No newline at end of file
+ 'logger',
+ 'http',
+ 'security'
+]
diff --git a/apimatic_core_interfaces/http/__init__.py b/apimatic_core_interfaces/http/__init__.py
new file mode 100644
index 0000000..3267c17
--- /dev/null
+++ b/apimatic_core_interfaces/http/__init__.py
@@ -0,0 +1,3 @@
+__all__ = [
+ 'request',
+]
\ No newline at end of file
diff --git a/apimatic_core_interfaces/http/request.py b/apimatic_core_interfaces/http/request.py
new file mode 100644
index 0000000..820372b
--- /dev/null
+++ b/apimatic_core_interfaces/http/request.py
@@ -0,0 +1,14 @@
+from dataclasses import dataclass, field
+from typing import Dict, List, Optional
+
+@dataclass(frozen=True)
+class Request:
+ """Framework-agnostic HTTP request snapshot (files excluded)."""
+ method: str
+ path: str
+ url: Optional[str]
+ headers: Dict[str, str]
+ raw_body: bytes
+ query: Dict[str, List[str]] = field(default_factory=dict)
+ cookies: Dict[str, str] = field(default_factory=dict)
+ form: Dict[str, List[str]] = field(default_factory=dict)
diff --git a/apimatic_core_interfaces/security/__init__.py b/apimatic_core_interfaces/security/__init__.py
new file mode 100644
index 0000000..07b716e
--- /dev/null
+++ b/apimatic_core_interfaces/security/__init__.py
@@ -0,0 +1,3 @@
+__all__ = [
+ 'signature_verifier'
+]
\ No newline at end of file
diff --git a/apimatic_core_interfaces/security/signature_verifier.py b/apimatic_core_interfaces/security/signature_verifier.py
new file mode 100644
index 0000000..24fb9bb
--- /dev/null
+++ b/apimatic_core_interfaces/security/signature_verifier.py
@@ -0,0 +1,29 @@
+from abc import ABC, abstractmethod
+
+from apimatic_core_interfaces.http.request import Request
+from apimatic_core_interfaces.types.signature_verification_result import SignatureVerificationResult
+
+
+class SignatureVerifier(ABC):
+ """
+ Abstract base class for signature verification.
+
+ Implementations must validate that the provided JSON payload matches
+ the signature contained in the headers.
+ """
+
+ @abstractmethod
+ def verify(self, request: Request) -> SignatureVerificationResult:
+ """
+ Perform signature verification.
+
+ Returns:
+ SignatureVerificationResult: ok=True when the signature is valid; ok=False with the
+ underlying exception (if any) when invalid or an error occurred.
+
+ Notes:
+ Implementations should NOT raise for runtime verification outcomes; return
+ VerificationResult.failed(error) instead. Reserve raising for programmer
+ errors (invalid construction/config).
+ """
+ raise NotImplementedError
diff --git a/apimatic_core_interfaces/types/__init__.py b/apimatic_core_interfaces/types/__init__.py
index 04023ac..c534d34 100644
--- a/apimatic_core_interfaces/types/__init__.py
+++ b/apimatic_core_interfaces/types/__init__.py
@@ -1,5 +1,6 @@
__all__ = [
'http_method_enum',
'authentication',
- 'union_type'
+ 'union_type',
+ 'signature_verification_result'
]
\ No newline at end of file
diff --git a/apimatic_core_interfaces/types/signature_verification_result.py b/apimatic_core_interfaces/types/signature_verification_result.py
new file mode 100644
index 0000000..77a8aee
--- /dev/null
+++ b/apimatic_core_interfaces/types/signature_verification_result.py
@@ -0,0 +1,26 @@
+from __future__ import annotations
+from typing import Optional, List
+
+
+class SignatureVerificationResult:
+ """
+ Outcome of signature verification.
+
+ Attributes:
+ ok: True if the signature verification passed.
+ errors: Optional list of errors raised by the verifier. None when ok=True.
+ """
+ ok: bool
+ errors: Optional[List[str]] = None
+
+ def __init__(self, ok: bool, errors: Optional[List[str]] = None):
+ self.ok = ok
+ self.errors = errors
+
+ @staticmethod
+ def passed() -> "SignatureVerificationResult":
+ return SignatureVerificationResult(ok=True, errors=None)
+
+ @staticmethod
+ def failed(errors: Optional[List[str]] = None) -> "SignatureVerificationResult":
+ return SignatureVerificationResult(ok=False, errors=errors)
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 881af1a..cfb7a70 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@
setup(
name='apimatic-core-interfaces',
- version='0.1.6',
+ version='0.1.7',
description='An abstract layer of the functionalities provided by apimatic-core-library, requests-client-adapter '
'and APIMatic SDKs.',
long_description=long_description,
diff --git a/sonar-project.properties b/sonar-project.properties
index 096b3e6..7fdf00f 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -4,4 +4,7 @@ sonar.organization=apimatic
sonar.host.url=https://sonarcloud.io
sonar.sourceEncoding=UTF-8
-sonar.sources=apimatic_core_interfaces
\ No newline at end of file
+sonar.sources=apimatic_core_interfaces
+
+# Skip coverage
+sonar.coverage.exclusions=**