Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ indent_size = 2

[*.py]
indent_size = 4

[{Makefile,*.mk}]
indent_style = tab
9 changes: 0 additions & 9 deletions .env.example

This file was deleted.

9 changes: 9 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MPT_API_BASE_URL=http://localhost:8000
MPT_API_TOKEN=<api-token>
# E2E env vars (only needed for development)
MPT_API_TOKEN_CLIENT=<client-api-token>
MPT_API_TOKEN_OPERATIONS=<operations-api-token>
MPT_API_TOKEN_VENDOR=<vendor-api-token>
RP_API_KEY=<rp-api-token>
RP_ENDPOINT=https://reportportal.example.com
RP_LAUNCH=dev-env
13 changes: 6 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ htmlcov/
.cache
nosetests.xml
coverage.xml
out.xml
*.cover
*.py,cover
.hypothesis/
Expand Down Expand Up @@ -159,17 +160,15 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.DS_Store

# ignore xlsx temporary files
~$*.xlsx
.devcontainer/
.vscode

# ruff cache
.DS_Store
.ruff_cache
.idea

# VS Code dev container
.devcontainer/
# Makefile
make/local.mk

# E2E report
e2e-report.xml
6 changes: 4 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ repos:
- id: flake8
additional_dependencies:
[
Flake8-pyproject==1.2.*,
Flake8-AAA==0.17.*,
flake8-aaa==0.17.*,
flake8-pyproject==1.2.*,
wemake-python-styleguide==1.5.*,
]

Expand All @@ -39,3 +39,5 @@ repos:
- id: mypy
args: ["--config-file=pyproject.toml", "."]
pass_filenames: false
additional_dependencies:
- python-box==7.3.2
24 changes: 20 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS base

WORKDIR /extension
WORKDIR /mpt_api_client

RUN uv venv /opt/venv

ENV VIRTUAL_ENV=/opt/venv
ENV UV_PROJECT_ENVIRONMENT=/opt/venv
ENV PATH=/opt/venv/bin:$PATH

FROM base AS build

COPY . /extension
COPY . .

RUN uv sync --frozen --no-cache --all-groups --active
RUN uv sync --frozen --no-cache --no-dev

FROM build AS dev

RUN uv sync --frozen --no-cache --dev

CMD ["bash"]

FROM build AS prod

RUN rm -rf tests/

RUN groupadd -r appuser && useradd -r -g appuser -m -d /home/appuser appuser && \
mkdir -p /home/appuser/.cache/uv && \
chown -R appuser:appuser /mpt_api_client /opt/venv /home/appuser

ENV UV_CACHE_DIR=/home/appuser/.cache/uv

USER appuser

CMD ["bash"]
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
MK_FILES := $(sort $(wildcard make/*.mk))
-include $(MK_FILES)

.DEFAULT_GOAL := help
.PHONY: $(shell awk -F: '/^[a-zA-Z0-9_-]+:([^=]|$$)/ {print $$1}' $(MAKEFILE_LIST))

require = $(if $(value $(1)),,$(error Missing required variable: $(1). Example: make $(MAKECMDGOALS) $(1)=<value>))

help: ## Show available commands
@echo "Available commands:"
@awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z0-9_-]+:.*##/ {printf " make %-22s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
127 changes: 93 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,125 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=softwareone-platform_mpt-api-python-client&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=softwareone-platform_mpt-api-python-client)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=softwareone-platform_mpt-api-python-client&metric=coverage)](https://sonarcloud.io/summary/new_code?id=softwareone-platform_mpt-api-python-client)

[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

# mpt-api-python-client

A Python client for interacting with the MPT API.

## Installation
## Documentation

📚 **[Complete Usage Guide](docs/PROJECT_DESCRIPTION.md)**

## Getting started

### Prerequisites

- Docker and Docker Compose plugin (`docker compose` CLI)
- `make`
- [CodeRabbit CLI](https://www.coderabbit.ai/cli) (optional. Used for running review check locally)

### Make targets overview

Common development workflows are wrapped in the `Makefile`. Run `make help` to see the list of available commands.

Install as a uv dependency:
### How the Makefile works

The project uses a modular Makefile structure that organizes commands into logical groups:
- **Main Makefile** (`Makefile`): Entry point that automatically includes all `.mk` files from the `make/` directory
- **Modular includes** (`make/*.mk`): Commands are organized by category:
- `common.mk` - Core development commands (build, test, format, etc.)
- `repo.mk` - Repository management and dependency commands
- `migrations.mk` - Database migration commands (Only available in extension repositories)
- `external_tools.mk` - Integration with external tools


You can extend the Makefile with your own custom commands creating a `local.mk` file inside make folder. This file is
automatically ignored by git, so your personal commands won't affect other developers or appear in version control.


### Setup

Follow these steps to set up the development environment:

#### 1. Clone the repository

```bash
uv add mpt-api-client
cp .env.example .env
git clone <repository-url>
```
```bash
cd mpt-api-python-client
```

## Usage

```python
from mpt_api_client import MPTClient
#### 2. Create environment configuration

#client = MPTClient(api_key=os.getenv("MPT_API_KEY"), base_url=os.getenv("MPT_API_URL"))
client = MPTClient() # Will get the api_key and base_url from the environment variables
Copy the sample environment file and update it with your values:

for product in client.catalog.products.iterate():
print(product.name)
```bash
cp .env.sample .env
```

## Async Usage
Edit the `.env` file with your actual configuration values. See the [Configuration](#configuration) section for details on available variables.

```python
import asyncio
from mpt_api_client import AsyncMPTClient
#### 3. Build the Docker images

async def main():
# client = AsyncMPTClient(api_key=os.getenv("MPT_API_KEY"), base_url=os.getenv("MPT_API_URL"))
client = AsyncMPTClient() # Will get the api_key and base_url from the environment variables
async for product in client.catalog.products.iterate():
print(product.name)
Build the development environment:

asyncio.run(main())
```bash
make build
```

## Development
This will create the Docker images with all required dependencies and the virtualenv.

#### 4. Verify the setup

Clone the repository and install dependencies:
Run the test suite to ensure everything is configured correctly:

```bash
git clone https://github.com/albertsola/mpt-api-python-client.git
cd mpt-api-python-client
uv add -r requirements.txt
make test
```

## Testing
You're now ready to start developing! See [Running the client](#running-the-client) for next steps.


Run all validations with:
## Running the client

Before running, ensure your `.env` file is populated.

```bash
make test-all
make run
```

Run pytest with:
## Developer utilities

Useful helper targets during development:

```bash
make test-all
make bash # open a bash shell in the app container
make check # run ruff, flake8, and lockfile checks
make check-all # run checks and tests
make format # auto-format code and imports
make review # check the code in the cli by running CodeRabbit
```
## License

MIT
## Configuration

The following environment variables are typically set in `.env`. Docker Compose reads them when using the Make targets described above.

### Application

| Environment Variable | Default | Example | Description |
|---------------------------------|---------|-------------------------------------------|-------------------------------------------------------------------------------------------|
| `MPT_API_BASE_URL` | - | `https://portal.softwareone.com/mpt` | SoftwareONE Marketplace API URL |
| `MPT_API_TOKEN` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Token |

### E2E

| Environment Variable | Default | Example | Description |
|----------------------------|---------|--------------------------------------|----------------------------------------------|
| `MPT_API_TOKEN_CLIENT` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Client Token |
| `MPT_API_TOKEN_OPERATIONS` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Operations Token |
| `MPT_API_TOKEN_VENDOR` | - | eyJhbGciOiJSUzI1N... | SoftwareONE Marketplace API Vendor Token |
| `RP_API_KEY` | - | pytest_XXXXXXXXXXXXXX | ReportPortal API key |
| `RP_ENDPOINT` | - | `https://reportportal.example.com` | ReportPortal endpoint |
| `RP_LAUNCH` | - | `dev-env` | ReportPortal launch |
50 changes: 50 additions & 0 deletions docs/PROJECT_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# mpt-api-python-client

mpt-api-python-client is a Python client for interacting with the MPT API

## Installation

Install with pip or your favorite PyPI package manager:

```bash
pip install mpt-api-client
```

```bash
uv add mpt-api-client
```

## Prerequisites

- Python 3.12+ in your environment

## Usage

```python
from mpt_api_client import MPTClient

# client = MPTClient(api_key=<your_api_key>, base_url=<mpt_api_url>)
client = MPTClient() # Reads MPT_API_TOKEN and MPT_API_BASE_URL from the environment

for product in client.catalog.products.iterate():
print(product.name)
```

## Async Usage

```python
import asyncio
from mpt_api_client import AsyncMPTClient

async def main():
# client = AsyncMPTClient(api_key=<your_api_key>, base_url=<mpt_api_url>)
client = AsyncMPTClient() # Reads MPT_API_TOKEN and MPT_API_BASE_URL from the environment
async for product in client.catalog.products.iterate():
print(product.name)

asyncio.run(main())
```

## Development

For development purposes, please, check the Readme in the GitHub repository.
40 changes: 40 additions & 0 deletions make/common.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
DC = docker compose -f compose.yaml
RUN = $(DC) run --rm app
RUN_IT = $(DC) run --rm -it app

bash: ## Open a bash shell
$(RUN_IT) bash

build: ## Build images
$(DC) build

check: ## Check code quality
$(RUN) bash -c "ruff format --check . && ruff check . && flake8 . && mypy . && uv lock --check"

check-all: check test ## Run checks and tests

down: ## Stop and remove containers
$(DC) down

format: ## Format code
$(RUN) bash -c "ruff check --select I --fix . && ruff format ."

run: ## Run service
$(RUN_IT) bash -c "ipython"

test: ## Run test
$(RUN) pytest $(if $(args),$(args), tests/unit)

uv-add: ## Add a production dependency (pkg=<package_name>)
$(call require,pkg)
$(RUN) bash -c "uv add $(pkg)"
$(MAKE) build

uv-add-dev: ## Add a dev dependency (pkg=<package_name>)
$(call require,pkg)
$(RUN) bash -c "uv add --dev $(pkg)"
$(MAKE) build

uv-upgrade: ## Upgrade all packages or a specific package (use pkg="package_name" to target one)
$(RUN) bash -c "uv lock $(if $(pkg),--upgrade-package $(pkg),--upgrade) && uv sync"
$(MAKE) build
2 changes: 2 additions & 0 deletions make/external_tools.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
review: ## Run CodeRabbit code review in interactive mode. Pass args=<options> to override or include more options
coderabbit review $(args)
3 changes: 3 additions & 0 deletions make/repo.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Add repo-specific targets here. Do not modify the shared *.mk files.
e2e: ## Run e2e test
$(RUN) pytest -p no:randomly --junitxml=e2e-report.xml $(if $(args),$(args), tests/e2e)
Loading