Skip to content
This repository was archived by the owner on Feb 18, 2026. It is now read-only.

Commit a37a50c

Browse files
committed
Upgrade python client generator to v7
1 parent dd6ecb7 commit a37a50c

233 files changed

Lines changed: 26453 additions & 30714 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,7 @@ local
9696
tests/test_loki_generated.py
9797
tests/loki_generated_calls.json
9898
tests/migration_timing_report.json
99+
clients/python/MIGRATION_ISSUES.md
100+
CRITICAL_ISSUES_SUMMARY.md
101+
COMPATIBILITY_ANALYSIS.md
102+
clients/python/COMPATIBILITY_QUICK_REFERENCE.md

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ NUM_THREADS ?= 1
1111

1212
# Migration test settings
1313
OLD_SERVER_PORT ?= 9001
14-
NEW_SERVER_PORT ?= 9003 # Note: adev uses OLD_SERVER_PORT+1 for aux/livereload server
14+
NEW_SERVER_PORT ?= 9000 # Note: adev uses OLD_SERVER_PORT+1 for aux/livereload server
1515
MIGRATION_BASE_REF ?= master
1616
WORKTREE_DIR ?= ../.graphsense-rest-old
1717

File renamed without changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# NOTE: This file is auto generated by OpenAPI Generator.
2+
# URL: https://openapi-generator.tech
3+
#
4+
# ref: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
5+
6+
name: graphsense Python package
7+
8+
on: [push, pull_request]
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
build:
15+
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v4
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
pip install -r requirements.txt
31+
pip install -r test-requirements.txt
32+
- name: Test with pytest
33+
run: |
34+
pytest --cov=graphsense

clients/python/.openapi-generator-ignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ setup.cfg
3333
requirements.txt
3434
test-requirements.txt
3535
setup.py
36+
pyproject.toml

clients/python/Makefile

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
-include .env
22

33
OPENAPI_URL ?= http://localhost:9000/openapi.json
4+
GENERATOR_VERSION ?= v7.19.0
5+
TEMPLATES_DIR ?= ../python-templates
46

5-
generate-openapi-client:
7+
# Legacy v5.2.1 generator (for comparison/rollback)
8+
generate-openapi-client-v5:
69
@echo "Downloading openapi.json from $(OPENAPI_URL)..."
710
curl -s $(OPENAPI_URL) > openapi.json.tmp
811
@echo "Modifying server URL to production..."
912
python scripts/openapi_spec_add_auth.py openapi.json.tmp https://api.ikna.io > openapi.json.modified
10-
# jq '.servers[0].url = "https://api.ikna.io"' openapi.json.tmp > openapi.json.modified
11-
@echo "Generating client..."
13+
@echo "Generating client with v5.2.1..."
1214
docker run --rm \
15+
--user $(shell id -u):$(shell id -g) \
1316
-v "${PWD}:/build:Z" \
14-
-v "${PWD}/templates:/templates:Z" \
17+
-v "${PWD}/$(TEMPLATES_DIR):/templates:Z" \
1518
openapitools/openapi-generator-cli:v5.2.1 \
1619
generate -i /build/openapi.json.modified \
1720
-g python \
@@ -22,7 +25,39 @@ generate-openapi-client:
2225
@echo "Cleaning up temporary files..."
2326
rm -f openapi.json.tmp openapi.json.modified
2427

28+
# v7 generator with backward compatibility patches
29+
generate-openapi-client:
30+
@echo "Downloading openapi.json from $(OPENAPI_URL)..."
31+
curl -s $(OPENAPI_URL) > openapi.json.tmp
32+
@echo "Modifying server URL to production..."
33+
python scripts/openapi_spec_add_auth.py openapi.json.tmp https://api.ikna.io > openapi.json.modified
34+
@API_VERSION=$$(jq -r '.info.version' openapi.json.tmp) && \
35+
echo "Generating client with $(GENERATOR_VERSION) (API version: $$API_VERSION)..." && \
36+
docker run --rm \
37+
--user $(shell id -u):$(shell id -g) \
38+
-v "${PWD}:/build:Z" \
39+
-v "${PWD}/$(TEMPLATES_DIR):/templates:Z" \
40+
openapitools/openapi-generator-cli:$(GENERATOR_VERSION) \
41+
generate -i /build/openapi.json.modified \
42+
-g python \
43+
-t /templates \
44+
-o /build \
45+
--additional-properties=packageName=graphsense \
46+
--additional-properties=projectName=graphsense-python \
47+
--additional-properties=packageVersion=$$API_VERSION \
48+
--additional-properties=library=urllib3
49+
@echo "Applying backward compatibility patches..."
50+
python scripts/patch_compat.py .
51+
@echo "Verifying backward compatibility..."
52+
python scripts/test_compat.py
53+
@echo "Cleaning up temporary files..."
54+
rm -f openapi.json.tmp openapi.json.modified
55+
56+
# Just run the compatibility tests (useful during development)
57+
test-compat:
58+
python scripts/test_compat.py
59+
2560
run-examples:
2661
API_KEY=$(API_KEY) python test_examples.py
2762

28-
.PHONY: generate-openapi-client run-examples
63+
.PHONY: generate-openapi-client generate-openapi-client-v5 test-compat run-examples

clients/python/README.md

Lines changed: 45 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ This Python package is automatically generated by the [OpenAPI Generator](https:
55

66
- API version: 1.16.0rc2
77
- Package version: 1.16.0rc2
8+
- Generator version: 7.19.0
89
- Build package: org.openapitools.codegen.languages.PythonClientCodegen
910

1011
## Requirements.
1112

12-
Python >= 3.6
13+
Python 3.9+
1314

1415
## Installation & Usage
1516
### pip install
1617

1718
If the python package is hosted on a repository, you can install directly using:
1819

1920
```sh
20-
pip install git+https://github.com/graphsense/graphsense-REST.git#subdirectory=clients/python
21+
pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git
2122
```
23+
(you may need to run `pip` with root permission: `sudo pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git`)
2224

2325
Then import the package:
2426
```python
@@ -39,25 +41,20 @@ Then import the package:
3941
import graphsense
4042
```
4143

44+
### Tests
45+
46+
Execute `pytest` to run the tests.
47+
4248
## Getting Started
4349

4450
Please follow the [installation procedure](#installation--usage) and then run the following:
4551

4652
```python
4753

48-
import time
4954
import graphsense
55+
from graphsense.rest import ApiException
5056
from pprint import pprint
51-
from graphsense.api import addresses_api
52-
from graphsense.model.address import Address
53-
from graphsense.model.address_tags import AddressTags
54-
from graphsense.model.address_txs import AddressTxs
55-
from graphsense.model.entity import Entity
56-
from graphsense.model.height import Height
57-
from graphsense.model.links import Links
58-
from graphsense.model.neighbor_addresses import NeighborAddresses
59-
from graphsense.model.related_addresses import RelatedAddresses
60-
from graphsense.model.tag_summary import TagSummary
57+
6158
# Defining the host is optional and defaults to https://api.ikna.io
6259
# See configuration.py for a list of all supported configuration parameters.
6360
configuration = graphsense.Configuration(
@@ -70,7 +67,7 @@ configuration = graphsense.Configuration(
7067
# satisfies your auth use case.
7168

7269
# Configure API key authorization: api_key
73-
configuration.api_key['api_key'] = 'YOUR_API_KEY'
70+
configuration.api_key['api_key'] = os.environ["API_KEY"]
7471

7572
# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
7673
# configuration.api_key_prefix['api_key'] = 'Bearer'
@@ -79,17 +76,19 @@ configuration.api_key['api_key'] = 'YOUR_API_KEY'
7976
# Enter a context with an instance of the API client
8077
with graphsense.ApiClient(configuration) as api_client:
8178
# Create an instance of the API class
82-
api_instance = addresses_api.AddressesApi(api_client)
83-
currency = "btc" # str | The cryptocurrency code (e.g., btc)
84-
address = "1Archive1n2C579dMsAu3iC6tWzuQJz8dN" # str | The cryptocurrency address
85-
include_actors = True # bool | Whether to include information about the actor behind the address (optional) (default to True)
79+
api_instance = graphsense.AddressesApi(api_client)
80+
currency = 'currency_example' # str | The cryptocurrency code (e.g., btc)
81+
address = 'address_example' # str | The cryptocurrency address
82+
include_actors = True # bool | Whether to include actor information (optional) (default to True)
8683

8784
try:
8885
# Get an address
8986
api_response = api_instance.get_address(currency, address, include_actors=include_actors)
87+
print("The response of AddressesApi->get_address:\n")
9088
pprint(api_response)
91-
except graphsense.ApiException as e:
89+
except ApiException as e:
9290
print("Exception when calling AddressesApi->get_address: %s\n" % e)
91+
9392
```
9493

9594
## Documentation for API Endpoints
@@ -107,44 +106,42 @@ Class | Method | HTTP request | Description
107106
*AddressesApi* | [**list_related_addresses**](docs/AddressesApi.md#list_related_addresses) | **GET** /{currency}/addresses/{address}/related_addresses | Get related addresses to the input address
108107
*AddressesApi* | [**list_tags_by_address**](docs/AddressesApi.md#list_tags_by_address) | **GET** /{currency}/addresses/{address}/tags | Get attribution tags for a given address
109108
*BlocksApi* | [**get_block**](docs/BlocksApi.md#get_block) | **GET** /{currency}/blocks/{height} | Get a block by its height
110-
*BlocksApi* | [**get_block_by_date**](docs/BlocksApi.md#get_block_by_date) | **GET** /{currency}/block_by_date/{date} | Get the closest blocks given a timestamp
109+
*BlocksApi* | [**get_block_by_date**](docs/BlocksApi.md#get_block_by_date) | **GET** /{currency}/block_by_date/{date} | Get block by date
111110
*BlocksApi* | [**list_block_txs**](docs/BlocksApi.md#list_block_txs) | **GET** /{currency}/blocks/{height}/txs | Get block transactions
112111
*BulkApi* | [**bulk_csv**](docs/BulkApi.md#bulk_csv) | **POST** /{currency}/bulk.csv/{operation} | Get data as CSV in bulk
113112
*BulkApi* | [**bulk_json**](docs/BulkApi.md#bulk_json) | **POST** /{currency}/bulk.json/{operation} | Get data as JSON in bulk
114113
*EntitiesApi* | [**get_entity**](docs/EntitiesApi.md#get_entity) | **GET** /{currency}/entities/{entity} | Get an entity
115114
*EntitiesApi* | [**list_address_tags_by_entity**](docs/EntitiesApi.md#list_address_tags_by_entity) | **GET** /{currency}/entities/{entity}/tags | Get address tags for a given entity
116115
*EntitiesApi* | [**list_entity_addresses**](docs/EntitiesApi.md#list_entity_addresses) | **GET** /{currency}/entities/{entity}/addresses | Get an entity's addresses
117116
*EntitiesApi* | [**list_entity_links**](docs/EntitiesApi.md#list_entity_links) | **GET** /{currency}/entities/{entity}/links | Get transactions between two entities
118-
*EntitiesApi* | [**list_entity_neighbors**](docs/EntitiesApi.md#list_entity_neighbors) | **GET** /{currency}/entities/{entity}/neighbors | Get an entity's direct neighbors
117+
*EntitiesApi* | [**list_entity_neighbors**](docs/EntitiesApi.md#list_entity_neighbors) | **GET** /{currency}/entities/{entity}/neighbors | Get an entity's neighbors in the entity graph
119118
*EntitiesApi* | [**list_entity_txs**](docs/EntitiesApi.md#list_entity_txs) | **GET** /{currency}/entities/{entity}/txs | Get all transactions an entity has been involved in
120-
*EntitiesApi* | [**search_entity_neighbors**](docs/EntitiesApi.md#search_entity_neighbors) | **GET** /{currency}/entities/{entity}/search | Search deeply for matching neighbors
119+
*EntitiesApi* | [**search_entity_neighbors**](docs/EntitiesApi.md#search_entity_neighbors) | **GET** /{currency}/entities/{entity}/search | Search neighbors of an entity
121120
*GeneralApi* | [**get_statistics**](docs/GeneralApi.md#get_statistics) | **GET** /stats | Get statistics of supported currencies
122121
*GeneralApi* | [**search**](docs/GeneralApi.md#search) | **GET** /search | Returns matching addresses, transactions and labels
123-
*RatesApi* | [**get_exchange_rates**](docs/RatesApi.md#get_exchange_rates) | **GET** /{currency}/rates/{height} | Returns exchange rate for a given height
124-
*TagsApi* | [**get_actor**](docs/TagsApi.md#get_actor) | **GET** /tags/actors/{actor} | Returns an actor given its unique id or (unique) label
125-
*TagsApi* | [**get_actor_tags**](docs/TagsApi.md#get_actor_tags) | **GET** /tags/actors/{actor}/tags | Returns the address tags for a given actor
126-
*TagsApi* | [**list_address_tags**](docs/TagsApi.md#list_address_tags) | **GET** /tags | Returns address tags associated with a given label
127-
*TagsApi* | [**list_concepts**](docs/TagsApi.md#list_concepts) | **GET** /tags/taxonomies/{taxonomy}/concepts | Returns the supported concepts of a taxonomy
128-
*TagsApi* | [**list_taxonomies**](docs/TagsApi.md#list_taxonomies) | **GET** /tags/taxonomies | Returns the supported taxonomies
129-
*TagsApi* | [**report_tag**](docs/TagsApi.md#report_tag) | **POST** /tags/report-tag | Users can use this endpoint to report a missing annotation.
130-
*TokensApi* | [**list_supported_tokens**](docs/TokensApi.md#list_supported_tokens) | **GET** /{currency}/supported_tokens | Returns a list of supported token (sub)currencies
131-
*TxsApi* | [**get_spending_txs**](docs/TxsApi.md#get_spending_txs) | **GET** /{currency}/txs/{tx_hash}/spending | Returns in which other transaction's outputs the asked transaction spent. Think backwards references is the transaction graph. This endpoint is only available for utxo like currencies.
132-
*TxsApi* | [**get_spent_in_txs**](docs/TxsApi.md#get_spent_in_txs) | **GET** /{currency}/txs/{tx_hash}/spent_in | Returns in which other transactions, outputs from the asked transaction are spent. Think forward references in the transaction graph. This endpoint is only available for utxo like currencies.
133-
*TxsApi* | [**get_tx**](docs/TxsApi.md#get_tx) | **GET** /{currency}/txs/{tx_hash} | Returns details of a specific transaction identified by its hash
134-
*TxsApi* | [**get_tx_conversions**](docs/TxsApi.md#get_tx_conversions) | **GET** /{currency}/txs/{tx_hash}/conversions | Returns conversion information (swaps or bridging txs) extracted from a specific transaction
135-
*TxsApi* | [**get_tx_io**](docs/TxsApi.md#get_tx_io) | **GET** /{currency}/txs/{tx_hash}/{io} | Returns input/output values of a specific transaction identified by its hash
122+
*RatesApi* | [**get_exchange_rates**](docs/RatesApi.md#get_exchange_rates) | **GET** /{currency}/rates/{height} | Get exchange rates for a given block height
123+
*TagsApi* | [**get_actor**](docs/TagsApi.md#get_actor) | **GET** /tags/actors/{actor} | Get an actor by ID
124+
*TagsApi* | [**get_actor_tags**](docs/TagsApi.md#get_actor_tags) | **GET** /tags/actors/{actor}/tags | Get tags associated with an actor
125+
*TagsApi* | [**list_address_tags**](docs/TagsApi.md#list_address_tags) | **GET** /tags | Get address tags by label
126+
*TagsApi* | [**list_concepts**](docs/TagsApi.md#list_concepts) | **GET** /tags/taxonomies/{taxonomy}/concepts | List concepts for a taxonomy
127+
*TagsApi* | [**list_taxonomies**](docs/TagsApi.md#list_taxonomies) | **GET** /tags/taxonomies | List all taxonomies
128+
*TagsApi* | [**report_tag**](docs/TagsApi.md#report_tag) | **POST** /tags/report-tag | Report a new tag
129+
*TokensApi* | [**list_supported_tokens**](docs/TokensApi.md#list_supported_tokens) | **GET** /{currency}/supported_tokens/ | Get supported tokens for a currency
130+
*TxsApi* | [**get_spending_txs**](docs/TxsApi.md#get_spending_txs) | **GET** /{currency}/txs/{tx_hash}/spending | Get transactions that this transaction is spending from
131+
*TxsApi* | [**get_spent_in_txs**](docs/TxsApi.md#get_spent_in_txs) | **GET** /{currency}/txs/{tx_hash}/spent_in | Get transactions that spent outputs from this transaction
132+
*TxsApi* | [**get_tx**](docs/TxsApi.md#get_tx) | **GET** /{currency}/txs/{tx_hash} | Get a transaction by its hash
133+
*TxsApi* | [**get_tx_conversions**](docs/TxsApi.md#get_tx_conversions) | **GET** /{currency}/txs/{tx_hash}/conversions | Get DeFi conversions for a transaction
134+
*TxsApi* | [**get_tx_io**](docs/TxsApi.md#get_tx_io) | **GET** /{currency}/txs/{tx_hash}/{io} | Get transaction inputs or outputs
136135
*TxsApi* | [**list_token_txs**](docs/TxsApi.md#list_token_txs) | **GET** /{currency}/token_txs/{tx_hash} | Returns all token transactions in a given transaction
137-
*TxsApi* | [**list_tx_flows**](docs/TxsApi.md#list_tx_flows) | **GET** /{currency}/txs/{tx_hash}/flows | Returns all asset flows / Internal txs and token flows within a given transaction
136+
*TxsApi* | [**list_tx_flows**](docs/TxsApi.md#list_tx_flows) | **GET** /{currency}/txs/{tx_hash}/flows | Get asset flows within a transaction
138137

139138

140139
## Documentation For Models
141140

142141
- [Actor](docs/Actor.md)
143142
- [ActorContext](docs/ActorContext.md)
144-
- [Actors](docs/Actors.md)
145143
- [Address](docs/Address.md)
146144
- [AddressTag](docs/AddressTag.md)
147-
- [AddressTagAllOf](docs/AddressTagAllOf.md)
148145
- [AddressTags](docs/AddressTags.md)
149146
- [AddressTx](docs/AddressTx.md)
150147
- [AddressTxUtxo](docs/AddressTxUtxo.md)
@@ -156,13 +153,14 @@ Class | Method | HTTP request | Description
156153
- [Entity](docs/Entity.md)
157154
- [EntityAddresses](docs/EntityAddresses.md)
158155
- [ExternalConversion](docs/ExternalConversion.md)
159-
- [Height](docs/Height.md)
156+
- [HTTPValidationError](docs/HTTPValidationError.md)
160157
- [LabelSummary](docs/LabelSummary.md)
161158
- [LabeledItemRef](docs/LabeledItemRef.md)
162-
- [LabeledItemRefs](docs/LabeledItemRefs.md)
163159
- [Link](docs/Link.md)
164160
- [LinkUtxo](docs/LinkUtxo.md)
165161
- [Links](docs/Links.md)
162+
- [LinksInner](docs/LinksInner.md)
163+
- [LocationInner](docs/LocationInner.md)
166164
- [NeighborAddress](docs/NeighborAddress.md)
167165
- [NeighborAddresses](docs/NeighborAddresses.md)
168166
- [NeighborEntities](docs/NeighborEntities.md)
@@ -173,8 +171,6 @@ Class | Method | HTTP request | Description
173171
- [RelatedAddresses](docs/RelatedAddresses.md)
174172
- [SearchResult](docs/SearchResult.md)
175173
- [SearchResultByCurrency](docs/SearchResultByCurrency.md)
176-
- [SearchResultLabels](docs/SearchResultLabels.md)
177-
- [SearchResultLeaf](docs/SearchResultLeaf.md)
178174
- [SearchResultLevel1](docs/SearchResultLevel1.md)
179175
- [SearchResultLevel2](docs/SearchResultLevel2.md)
180176
- [SearchResultLevel3](docs/SearchResultLevel3.md)
@@ -188,54 +184,33 @@ Class | Method | HTTP request | Description
188184
- [Taxonomy](docs/Taxonomy.md)
189185
- [TokenConfig](docs/TokenConfig.md)
190186
- [TokenConfigs](docs/TokenConfigs.md)
191-
- [TokenValues](docs/TokenValues.md)
192187
- [Tx](docs/Tx.md)
193188
- [TxAccount](docs/TxAccount.md)
194189
- [TxRef](docs/TxRef.md)
195190
- [TxSummary](docs/TxSummary.md)
196191
- [TxUtxo](docs/TxUtxo.md)
197192
- [TxValue](docs/TxValue.md)
198-
- [TxValues](docs/TxValues.md)
199-
- [Txs](docs/Txs.md)
200-
- [TxsAccount](docs/TxsAccount.md)
201193
- [UserReportedTag](docs/UserReportedTag.md)
202194
- [UserTagReportResponse](docs/UserTagReportResponse.md)
195+
- [ValidationError](docs/ValidationError.md)
203196
- [Values](docs/Values.md)
204197

205198

199+
<a id="documentation-for-authorization"></a>
206200
## Documentation For Authorization
207201

208202

209-
## api_key
203+
Authentication schemes defined for the API:
204+
<a id="api_key"></a>
205+
### api_key
210206

211207
- **Type**: API key
212208
- **API key parameter name**: Authorization
213209
- **Location**: HTTP header
214210

215211

216-
## Examples
217-
218-
In `./examples` you can find example Python scripts and [Jupyter](https://jupyter.org/) notebooks demonstrating how to use the GraphSense Python API. Please Follow these setup instructions to run them:
219-
220-
Setup a Python environment with [Anaconda](https://www.anaconda.com/products/distribution):
221-
222-
conda env create -f environment.yml
223-
conda activate graphsense-python
212+
## Author
224213

225-
Copy the config temp file and enter your Iknaio API key
214+
contact@ikna.io
226215

227-
cp config.json.tmp config.json
228-
vi config.json
229216

230-
Run the jupyter notebooks
231-
232-
jupyter notebook
233-
234-
235-
## Generation from OpenAPI specification
236-
237-
This python package has been generated from [Graphsense's OpenAPI specification](https://api.ikna.io) hosted by [Iknaio Cryptoasset Analytics GmbH](https://ikna.io) using this command:
238-
239-
```
240-
make generate-openapi-client
241-
```

0 commit comments

Comments
 (0)