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
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
tests:
name: PHP ${{ matrix.php }} (${{ matrix.dependencies }} dependencies)
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
# highest: Symfony 7.4 on PHP 8.2/8.3, Symfony 8 on PHP 8.4+
php: ['8.2', '8.3', '8.4', '8.5']
dependencies: [highest]
include:
# lowest: Symfony 6.4, PSR-7 v1.1, guzzlehttp/psr7 v2
- php: '8.2'
dependencies: lowest

steps:
- uses: actions/checkout@v5

- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none

- run: composer validate --strict --no-check-lock

- uses: ramsey/composer-install@v3
with:
dependency-versions: ${{ matrix.dependencies }}

- run: vendor/bin/phpunit
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ composer.lock

# PHPUnit cache
.phpunit.result.cache
.phpunit.cache/
8 changes: 8 additions & 0 deletions .openapi-generator-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

# Hand-maintained files: keep them when regenerating the SDK
.gitignore
.php-cs-fixer.dist.php
.travis.yml
README.md
composer.json
phpunit.xml.dist
4 changes: 3 additions & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
;

$config = new PhpCsFixer\Config();
return $config->setRules([
return $config
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'phpdoc_order' => true,
'array_syntax' => [ 'syntax' => 'short' ],
Expand Down
8 changes: 0 additions & 8 deletions .travis.yml

This file was deleted.

126 changes: 86 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# OpenAPIClient-php
# eConnect PSB PHP SDK

PHP SDK for econnect PSB (https://psb.econnect.eu/?urls.primaryName=V1)
PHP SDK for the eConnect Procurement Service Bus (PSB) API (https://psb.econnect.eu/?urls.primaryName=V1)


## Installation & Usage

### Requirements

PHP 8.2 and later.
- PHP 8.2 or later
- A [PSR-18](https://packagist.org/providers/psr/http-client-implementation) HTTP client with an [HTTPlug async](https://packagist.org/providers/php-http/async-client-implementation) implementation, such as Symfony HttpClient or Guzzle
- Compatible with Symfony 6.4, 7.x and 8.x (Symfony 8 itself requires PHP 8.4 or later)

### Composer

Expand All @@ -22,23 +24,23 @@ To install the bindings via [Composer](https://getcomposer.org/), add the follow
}
],
"require": {
"QbilSoftware/econnect-psb-php": "@dev-main"
"qbil-software/econnect-psb-php": "dev-main"
}
}
```

Then run `composer install`
Then run `composer install`.

Your project is free to choose the http client of your choice
Please require packages that will provide http client functionality:
https://packagist.org/providers/psr/http-client-implementation
https://packagist.org/providers/php-http/async-client-implementation
https://packagist.org/providers/psr/http-factory-implementation
Your project is free to choose the HTTP client of your choice. In a Symfony application, use Symfony HttpClient:

As an example:
```
composer require symfony/http-client guzzlehttp/promises
```

Or use Guzzle 7 with its HTTPlug adapter:

```
composer require guzzlehttp/guzzle php-http/guzzle7-adapter http-interop/http-factory-guzzle
composer require guzzlehttp/guzzle php-http/guzzle7-adapter
```

### Manual Installation
Expand All @@ -47,7 +49,7 @@ Download the files and include `autoload.php`:

```php
<?php
require_once('/path/to/OpenAPIClient-php/vendor/autoload.php');
require_once('/path/to/econnect-psb-php/vendor/autoload.php');
```

## Getting Started
Expand All @@ -58,47 +60,66 @@ Please follow the [installation procedure](#installation--usage) and then run th
<?php
require_once(__DIR__ . '/vendor/autoload.php');



// Configure API key authorization: Subscription-Key
$config = EConnect\Psb\Configuration::getDefaultConfiguration()->setApiKey('Subscription-Key', 'YOUR_API_KEY');
// Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
// $config = EConnect\Psb\Configuration::getDefaultConfiguration()->setApiKeyPrefix('Subscription-Key', 'Bearer');

$config
->setUsername("{username}")
->setPassword("{password}");
->setHost("https://psb.econnect.eu")
$config = (new EConnect\Psb\Configuration())
->setHost('https://psb.econnect.eu')
->setUsername('{username}')
->setPassword('{password}')
->setApiKey('Subscription-Key', '{subscription key}');
$authN = new \EConnect\Psb\OidcAuthenticator(
\Http\Discovery\Psr18ClientDiscovery::find(),
\Http\Discovery\Psr17FactoryDiscovery::findRequestFactory(),
\Http\Discovery\Psr17FactoryDiscovery::findStreamFactory(),
new \Symfony\Component\Clock\NativeClock(),

$authenticator = new EConnect\Psb\OidcAuthenticator(
Http\Discovery\Psr18ClientDiscovery::find(),
Http\Discovery\Psr17FactoryDiscovery::findRequestFactory(),
Http\Discovery\Psr17FactoryDiscovery::findStreamFactory(),
new Symfony\Component\Clock\NativeClock(),
$config,
);
$authN->login('{clientId}', '{clientSecret}');

$apiInstance = new EConnect\Psb\Api\HookApi(
// If you want use custom http client, pass your client which implements `Psr\Http\Client\ClientInterface`.
// This is optional, `Psr18ClientDiscovery` will be used to find http client. For instance `GuzzleHttp\Client` implements that interface
\Http\Discovery\Psr18ClientDiscovery::find(),
$config
);
// Fetches an access token and stores it in $config. The token is cached and renewed
// shortly before it expires, so it is cheap to call this before every batch of API calls.
$authenticator->authenticate('{clientId}', '{clientSecret}');

// The HTTP client is optional: when omitted, it is discovered with `Psr18ClientDiscovery`.
$apiInstance = new EConnect\Psb\Api\HookApi(null, $config);

try {
$result = $apiInstance->getHookConfigs();
print_r($result);
} catch (Exception $e) {
} catch (EConnect\Psb\ApiException $e) {
echo 'Exception when calling HookApi->getHookConfigs: ', $e->getMessage(), PHP_EOL;
}
```

### Symfony

With `symfony/framework-bundle` and `symfony/http-client` installed, register the SDK as services, for example in `config/services.yaml`:

```yaml
services:
EConnect\Psb\Configuration:
calls:
- setHost: ['%env(PSB_HOST)%']
- setUsername: ['%env(PSB_USERNAME)%']
- setPassword: ['%env(PSB_PASSWORD)%']
- setApiKey: ['Subscription-Key', '%env(PSB_SUBSCRIPTION_KEY)%']

EConnect\Psb\OidcAuthenticator:
arguments:
$httpClient: '@psr18.http_client'
$requestFactory: '@psr18.http_client'
$streamFactory: '@psr18.http_client'
$clock: '@clock'
$config: '@EConnect\Psb\Configuration'

EConnect\Psb\Api\:
resource: '%kernel.project_dir%/vendor/qbil-software/econnect-psb-php/lib/Api/'
autowire: true
```

The API classes then use Symfony HttpClient for both synchronous and asynchronous requests. Inject `OidcAuthenticator` and the API classes you need into your own services, and call `authenticate()` before using the APIs.

## API Endpoints

All URIs are relative to *http://localhost*
All URIs are relative to the configured host, e.g. *https://psb.econnect.eu*

Class | Method | HTTP request | Description
------------ | ------------- | ------------- | -------------
Expand All @@ -112,6 +133,13 @@ Class | Method | HTTP request | Description
*MeApi* | [**getUser**](docs/Api/MeApi.md#getuser) | **GET** /api/v1/me | Get user.
*MeApi* | [**getUserParty**](docs/Api/MeApi.md#getuserparty) | **GET** /api/v1/me/party | Get parties.
*PeppolApi* | [**getDeliveryOptions**](docs/Api/PeppolApi.md#getdeliveryoptions) | **GET** /api/v1/peppol/deliveryOption | Advanced recipient party lookup in Peppol.
*PeppolApi* | [**getPeppolConfig**](lib/Api/PeppolApi.php) | **GET** /api/v1/peppol/config | Get default Peppol configuration
*PeppolApi* | [**getPeppolParties**](lib/Api/PeppolApi.php) | **GET** /api/v1/peppol/config/party | Get your Peppol participants
*PeppolApi* | [**getPeppolPartyConfig**](lib/Api/PeppolApi.php) | **GET** /api/v1/peppol/config/party/{partyId} | Get participant Peppol configuration
*PeppolApi* | [**removePeppolPartyConfig**](lib/Api/PeppolApi.php) | **DELETE** /api/v1/peppol/config/party/{partyId} | Un register (Remove) participant in Peppol
*PeppolApi* | [**requestPartyPrepareToMigrate**](lib/Api/PeppolApi.php) | **GET** /api/v1/peppol/{partyId}/prepareToMigrate | Generates a migration key and registers it with the SML to enable participant transfer between SMPs
*PeppolApi* | [**setPeppolConfig**](lib/Api/PeppolApi.php) | **PUT** /api/v1/peppol/config | Add or update default Peppol configuration
*PeppolApi* | [**setPeppolPartyConfig**](lib/Api/PeppolApi.php) | **PUT** /api/v1/peppol/config/party/{partyId} | Register or update participant in Peppol
*PurchaseInvoiceApi* | [**deletePurchaseInvoice**](docs/Api/PurchaseInvoiceApi.md#deletepurchaseinvoice) | **DELETE** /api/v1/{partyId}/purchaseInvoice/{documentId} | Delete purchase invoice.
*PurchaseInvoiceApi* | [**downloadPurchaseInvoice**](docs/Api/PurchaseInvoiceApi.md#downloadpurchaseinvoice) | **GET** /api/v1/{partyId}/purchaseInvoice/{documentId}/download | Download purchase invoice.
*PurchaseInvoiceApi* | [**getPurchaseInvoiceStatuses**](docs/Api/PurchaseInvoiceApi.md#getpurchaseinvoicestatuses) | **GET** /api/v1/{partyId}/purchaseInvoice/{documentId}/status | Get purchase invoice statuses (FOR DEBUGGING ONLY).
Expand Down Expand Up @@ -140,6 +168,7 @@ Class | Method | HTTP request | Description

## Models

- [BusinessCardProperty](lib/Model/BusinessCardProperty.php)
- [DeliveryOption](docs/Model/DeliveryOption.md)
- [Document](docs/Model/Document.md)
- [DocumentFamily](docs/Model/DocumentFamily.md)
Expand All @@ -157,7 +186,12 @@ Class | Method | HTTP request | Description
- [Party](docs/Model/Party.md)
- [PartyPagedResult](docs/Model/PartyPagedResult.md)
- [PartyPermissions](docs/Model/PartyPermissions.md)
- [PeppolBusinessCard](lib/Model/PeppolBusinessCard.php)
- [PeppolCapability](lib/Model/PeppolCapability.php)
- [PeppolConfig](lib/Model/PeppolConfig.php)
- [PeppolMigrateConfig](docs/Model/PeppolMigrateConfig.md)
- [PeppolPartyConfig](lib/Model/PeppolPartyConfig.php)
- [PeppolPartyVerification](lib/Model/PeppolPartyVerification.php)
- [PrepareToMigrateResponse](docs/Model/PrepareToMigrateResponse.md)
- [ProcessId](docs/Model/ProcessId.md)
- [TransportProtocol](docs/Model/TransportProtocol.md)
Expand Down Expand Up @@ -189,9 +223,21 @@ To run the tests, use:

```bash
composer install
vendor/bin/phpunit
composer test
```

The `unit` test suite in `test/Unit` covers authentication, (de)serialization and request building, including a run on top of Symfony HttpClient. The `generated` suite in `test/Api` and `test/Model` contains the placeholders created by OpenAPI Generator.

## Regenerating the SDK

Hand-maintained files are listed in `.openapi-generator-ignore` and are kept when regenerating. The generated code also carries the following patches, which have to be re-applied after regenerating:

- `lib/Api/*.php`: `createRequest()` always builds the request through the PSR-17 factories, and multipart requests set `Content-Type: multipart/form-data; boundary=...` from the `MultipartStream`.
- `lib/ObjectSerializer.php`: dates are detected with `\DateTimeInterface`, so `\DateTimeImmutable` values are serialized as well.
- `lib/DebugPlugin.php`: failed requests are logged as `<METHOD path HTTP/version> code message`.

`lib/functions.php` provides `GuzzleHttp\Psr7\try_fopen()`, which the generated file uploads call but `guzzlehttp/psr7` 2.0 and later no longer ship.

## Author

techsupport@econnect.eu
Expand Down
45 changes: 30 additions & 15 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
{
"name": "qbil-software/econnect-psb-php",
"description": "No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)",
"description": "PHP SDK for the eConnect Procurement Service Bus (PSB) API",
"type": "library",
"keywords": [
"openapitools",
"openapi-generator",
"econnect",
"psb",
"peppol",
"e-invoicing",
"openapi",
"php",
"sdk",
"rest",
"api"
"api",
"symfony"
],
"homepage": "https://openapi-generator.tech",
"license": "unlicense",
"homepage": "https://github.com/QbilSoftware/econnect-psb-php",
"license": "MIT",
"authors": [
{
"name": "Qbil Software",
"homepage": "https://www.qbilsoftware.com"
},
{
"name": "OpenAPI-Generator contributors",
"homepage": "https://openapi-generator.tech"
Expand All @@ -26,31 +34,38 @@
},
"require": {
"php": "^8.2",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/psr7": "^1.8 || ^2.0",
"guzzlehttp/psr7": "^2.12.3 || ^3.0",
"php-http/async-client-implementation": "^1.0",
"php-http/client-common": "^2.4",
"php-http/discovery": "^1.14",
"php-http/discovery": "^1.15",
"php-http/httplug": "^2.2",
"psr/clock": "^1.0",
"psr/http-client": "^1.0",
"psr/http-client-implementation": "^1.0",
"psr/http-factory": "^1.0",
"psr/http-factory-implementation": "^1.0",
"psr/http-message": "^1.0",
"symfony/clock": "^7.2"
"psr/http-message": "^1.1 || ^2.0",
"symfony/clock": "^6.4 || ^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0",
"friendsofphp/php-cs-fixer": "^v3.7",
"guzzlehttp/guzzle": "^7.0",
"php-http/guzzle7-adapter": "^1.0"
"friendsofphp/php-cs-fixer": "^3.7",
"guzzlehttp/promises": "^2.0 || ^3.0",
"php-http/mock-client": "^1.6",
"phpunit/phpunit": "^11.5 || ^12.0 || ^13.0",
"symfony/http-client": "^6.4 || ^7.0 || ^8.0"
},
"autoload": {
"psr-4": { "EConnect\\Psb\\" : "lib/" },
"files": ["lib/functions.php"]
},
"autoload-dev": {
"psr-4": { "EConnect\\Psb\\Test\\" : "test/" }
},
"scripts": {
"test": "phpunit",
"cs-check": "php-cs-fixer fix --dry-run --diff",
"cs-fix": "php-cs-fixer fix"
}
}
2 changes: 1 addition & 1 deletion lib/DebugPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private function logError(RequestInterface $request, ClientExceptionInterface $e
$protocol = $request->getProtocolVersion();
$error = $exception->getMessage();
$responseCode = $exception->getCode();
\fprintf($this->output, '<%s HTTP/%s> %s %s', $methodAndPath, $responseCode, $error, $protocol);
\fprintf($this->output, '<%s HTTP/%s> %s %s', $methodAndPath, $protocol, $responseCode, $error);
\fwrite($this->output, "\n");
}
}
Loading
Loading