mirror of
https://github.com/ivuorinen/business-data-fetcher.git
synced 2026-03-17 19:00:44 +00:00
Compare commits
17 Commits
v1.1.0
...
faf0354967
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
faf0354967 | ||
|
|
13b64a77fb | ||
|
|
5ed216871e | ||
|
|
ea3daeb23f | ||
| 150c466368 | |||
|
|
f025e1202d | ||
|
|
a03878345c | ||
|
|
ed7ed02359 | ||
|
|
f41ca45f8d | ||
|
|
2b3a8ddf5f | ||
|
|
9e3b051e29 | ||
| 889361eef4 | |||
|
|
57159e0678 | ||
|
|
f002004a04 | ||
|
|
687c21fa65 | ||
| e06f28ae2a | |||
| 2e08598832 |
31
.claude/settings.json
Normal file
31
.claude/settings.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); fp=$(echo \"$input\" | jq -r \".tool_input.file_path\"); if [[ \"$fp\" == *composer.lock ]] || [[ \"$fp\" == vendor/* ]] || [[ \"$fp\" == */vendor/* ]]; then echo \"{\\\"decision\\\":\\\"deny\\\",\\\"reason\\\":\\\"Do not edit $fp directly\\\"}\" >&2; exit 2; fi'"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); fp=$(echo \"$input\" | jq -r \".tool_input.file_path\"); if [[ \"$fp\" == *.php ]]; then cd \"$CLAUDE_PROJECT_DIR\" && php vendor/bin/phpcbf --standard=PSR12 \"$fp\" 2>/dev/null; fi; true'"
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash -c 'input=$(cat); fp=$(echo \"$input\" | jq -r \".tool_input.file_path\"); if [[ \"$fp\" == *.php ]]; then cd \"$CLAUDE_PROJECT_DIR\" && composer phpstan 2>&1 | tail -5; fi; true'",
|
||||
"timeout": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
13
.claude/skills/check-quality/SKILL.md
Normal file
13
.claude/skills/check-quality/SKILL.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: check-quality
|
||||
description: Run lint, phpstan, and tests in sequence, fix any issues found
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Run the full quality check pipeline:
|
||||
1. `composer lint` — fix any PSR-12 violations with `composer lint-fix`
|
||||
2. `composer phpstan` — fix any static analysis issues
|
||||
3. `composer test` — fix any failing tests
|
||||
|
||||
After each step, if issues are found, fix them before proceeding to the next step.
|
||||
Do not commit. Report results when done.
|
||||
16
.claude/skills/gen-test/SKILL.md
Normal file
16
.claude/skills/gen-test/SKILL.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: gen-test
|
||||
description: Generate a Pest test for a PHP class following project conventions
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Generate a Pest test file for the specified class.
|
||||
|
||||
Rules:
|
||||
- Place tests in `tests/Unit/` mirroring the src/ structure
|
||||
- Use Pest syntax (test(), expect(), describe())
|
||||
- For DTOs: test construction with valid data, verify all readonly properties
|
||||
- For traits: test each method the trait provides
|
||||
- For clients: mock Guzzle responses, test Valinor hydration
|
||||
- Follow existing test patterns in tests/Unit/
|
||||
- Read existing tests first to match style
|
||||
15
.claude/skills/new-dto/SKILL.md
Normal file
15
.claude/skills/new-dto/SKILL.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
name: new-dto
|
||||
description: Create a new final readonly DTO class with Valinor-compatible constructor
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
Create a new DTO class following project conventions:
|
||||
- `final readonly class` with constructor promotion
|
||||
- Place in `src/v1/Dto/` or `src/v3/Dto/` based on API version
|
||||
- All properties as constructor-promoted readonly parameters
|
||||
- Use appropriate PHP types (string, int, ?string for nullable)
|
||||
- Apply relevant traits (HasSource, HasLanguage, etc.) if applicable
|
||||
- Namespace: `Ivuorinen\BusinessDataFetcher\{v1|v3}\Dto`
|
||||
- Generate a corresponding Pest test in tests/Unit/
|
||||
- Read existing DTOs first to match style
|
||||
28
.github/labels.yml
vendored
Normal file
28
.github/labels.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
- name: bug
|
||||
color: d73a4a
|
||||
description: Something isn't working
|
||||
|
||||
- name: enhancement
|
||||
color: a2eeef
|
||||
description: New feature or request
|
||||
|
||||
- name: documentation
|
||||
color: 0075ca
|
||||
description: Improvements or additions to documentation
|
||||
|
||||
- name: dependencies
|
||||
color: 0366d6
|
||||
description: Pull requests that update a dependency file
|
||||
|
||||
- name: breaking-change
|
||||
color: e11d48
|
||||
description: Breaking change to public API
|
||||
|
||||
- name: maintenance
|
||||
color: fbca04
|
||||
description: Code maintenance and refactoring
|
||||
|
||||
- name: ci
|
||||
color: 7057ff
|
||||
description: Continuous integration and tooling
|
||||
2
.github/renovate.json
vendored
2
.github/renovate.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["github>ivuorinen/.github:renovate-config"]
|
||||
"extends": ["github>ivuorinen/renovate-config"]
|
||||
}
|
||||
|
||||
49
.github/workflows/composer.yml
vendored
49
.github/workflows/composer.yml
vendored
@@ -14,37 +14,40 @@ jobs:
|
||||
build:
|
||||
|
||||
# pre-installed php from 7.4 to 8.3
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-versions: ['8.2', '8.3']
|
||||
php-versions: ['8.2', '8.3', '8.4']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
- uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # 2.37.0
|
||||
with:
|
||||
php-version: ${{ matrix.php-versions }}
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate --strict
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate --strict
|
||||
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vendor
|
||||
key: ${{ runner.os }}-php-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-php-${{ matrix.php-versions }}-
|
||||
${{ runner.os }}-php-
|
||||
- name: Cache Composer packages
|
||||
id: composer-cache
|
||||
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||
with:
|
||||
path: ~/.composer/cache
|
||||
key: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-composer-${{ matrix.php-versions }}-
|
||||
${{ runner.os }}-composer-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress
|
||||
|
||||
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
|
||||
# Docs: https://getcomposer.org/doc/articles/scripts.md
|
||||
- name: Run linting
|
||||
run: composer lint
|
||||
|
||||
# - name: Run test suite
|
||||
# run: composer run-script test
|
||||
- name: Run static analysis
|
||||
run: composer phpstan
|
||||
|
||||
- name: Run test suite
|
||||
run: composer test
|
||||
|
||||
17
.github/workflows/pr-lint.yml
vendored
17
.github/workflows/pr-lint.yml
vendored
@@ -2,12 +2,19 @@
|
||||
name: PR Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [master, main]
|
||||
# Remove the line above to run when pushing to master
|
||||
pull_request:
|
||||
branches: [master, main]
|
||||
|
||||
jobs:
|
||||
SuperLinter:
|
||||
uses: ivuorinen/.github/.github/workflows/pr-lint.yml@main
|
||||
lint:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ivuorinen/actions/pr-lint@1da3a0e79fcd7da6bed9ee1979f1449ba11f58f9 # v2026.03.14
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
14
.github/workflows/release-drafter.yml
vendored
14
.github/workflows/release-drafter.yml
vendored
@@ -1,10 +1,18 @@
|
||||
---
|
||||
name: Release Drafter
|
||||
name: Release Monthly
|
||||
|
||||
# yamllint disable-line rule:truthy
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
Draft:
|
||||
uses: ivuorinen/.github/.github/workflows/sync-labels.yml@main
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ivuorinen/actions/release-monthly@1da3a0e79fcd7da6bed9ee1979f1449ba11f58f9 # v2026.03.14
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
8
.github/workflows/stale.yml
vendored
8
.github/workflows/stale.yml
vendored
@@ -9,10 +9,14 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write # only for delete-branch option
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
uses: ivuorinen/.github/.github/workflows/stale.yml@main
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ivuorinen/actions/stale@1da3a0e79fcd7da6bed9ee1979f1449ba11f58f9 # v2026.03.14
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
8
.github/workflows/sync-labels.yml
vendored
8
.github/workflows/sync-labels.yml
vendored
@@ -18,4 +18,10 @@ permissions:
|
||||
|
||||
jobs:
|
||||
SyncLabels:
|
||||
uses: ivuorinen/.github/.github/workflows/sync-labels.yml@main
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: ivuorinen/actions/sync-labels@1da3a0e79fcd7da6bed9ee1979f1449ba11f58f9 # v2026.03.14
|
||||
with:
|
||||
labels: .github/labels.yml
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
124
.gitignore
vendored
124
.gitignore
vendored
@@ -1,90 +1,49 @@
|
||||
.php-cs-fixer.cache
|
||||
.php-cs-fixer.php
|
||||
# PHP / Composer
|
||||
composer.phar
|
||||
/vendor/
|
||||
|
||||
# PHP CS Fixer
|
||||
.php-cs-fixer.cache
|
||||
.php-cs-fixer.php
|
||||
|
||||
# PHPUnit / Pest
|
||||
.phpunit.result.cache
|
||||
.phpunit.cache
|
||||
/app/phpunit.xml
|
||||
/phpunit.xml
|
||||
|
||||
# Build artifacts
|
||||
/build/
|
||||
logs
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
lib-cov
|
||||
coverage
|
||||
*.lcov
|
||||
.nyc_output
|
||||
.grunt
|
||||
bower_components
|
||||
.lock-wscript
|
||||
build/Release
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
web_modules/
|
||||
*.tsbuildinfo
|
||||
.npm
|
||||
.eslintcache
|
||||
.stylelintcache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
.node_repl_history
|
||||
*.tgz
|
||||
.yarn-integrity
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
.cache
|
||||
.parcel-cache
|
||||
.next
|
||||
out
|
||||
.nuxt
|
||||
dist
|
||||
.cache/
|
||||
.vuepress/dist
|
||||
.temp
|
||||
.docusaurus
|
||||
.serverless/
|
||||
.fusebox/
|
||||
.dynamodb/
|
||||
.tern-port
|
||||
.vscode-test
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
.env*.local
|
||||
|
||||
# OS / Editor
|
||||
.DS_Store
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
!*.svg
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
*~
|
||||
[._]*.un~
|
||||
|
||||
# Vim
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
.netrwhist
|
||||
*~
|
||||
tags
|
||||
[._]*.un~
|
||||
|
||||
# JetBrains (PHPStorm)
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
.idea/**/aws.xml
|
||||
.idea/**/contentModel.xml
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
@@ -92,44 +51,13 @@ tags
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
cmake-build-*/
|
||||
.idea/**/mongoSettings.xml
|
||||
*.iws
|
||||
out/
|
||||
.idea_modules/
|
||||
atlassian-ide-plugin.xml
|
||||
.idea/replstate.xml
|
||||
.idea/sonarlint/
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
.idea/httpRequests
|
||||
.idea/caches/build_file_checksums.ser
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
bootstrap/compiled.php
|
||||
app/storage/
|
||||
public/storage
|
||||
public/hot
|
||||
public_html/storage
|
||||
public_html/hot
|
||||
storage/*.key
|
||||
Homestead.yaml
|
||||
Homestead.json
|
||||
/.vagrant
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
/coverage
|
||||
/.next/
|
||||
/out/
|
||||
/build
|
||||
.DS_Store
|
||||
*.pem
|
||||
.env*.local
|
||||
.vercel
|
||||
next-env.d.ts
|
||||
|
||||
# Claude Code
|
||||
.claude/settings.local.json
|
||||
|
||||
4
.markdownlint.json
Normal file
4
.markdownlint.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"MD013": false,
|
||||
"MD041": false
|
||||
}
|
||||
1
.markdownlintignore
Normal file
1
.markdownlintignore
Normal file
@@ -0,0 +1 @@
|
||||
docs.md
|
||||
26
.mega-linter.yml
Normal file
26
.mega-linter.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
APPLY_FIXES: all
|
||||
PARALLEL: true
|
||||
VALIDATE_ALL_CODEBASE: true
|
||||
GITHUB_STATUS_REPORTER: true
|
||||
SARIF_REPORTER: true
|
||||
IGNORE_GENERATED_FILES: true
|
||||
PRINT_ALPACA: false
|
||||
SHOW_SKIPPED_LINTERS: false
|
||||
SHOW_ELAPSED_TIME: false
|
||||
FILEIO_REPORTER: false
|
||||
|
||||
ENABLE_LINTERS:
|
||||
- YAML_YAMLLINT
|
||||
- MARKDOWN_MARKDOWNLINT
|
||||
- PHP_PHPCS
|
||||
|
||||
DISABLE_LINTERS:
|
||||
- REPOSITORY_DEVSKIM
|
||||
- JSON_PRETTIER
|
||||
- YAML_PRETTIER
|
||||
|
||||
PHP_PHPCS_CLI_LINT_MODE: project
|
||||
PHP_PHPCS_ARGUMENTS: "--warning-severity=0"
|
||||
|
||||
FILTER_REGEX_EXCLUDE: "(vendor/|node_modules/|\\.git/|\\.claude/|composer\\.lock|package-lock\\.json)"
|
||||
20
.yamllint.yml
Normal file
20
.yamllint.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
comments:
|
||||
require-starting-space: true
|
||||
ignore-shebangs: true
|
||||
min-spaces-from-content: 1
|
||||
line-length:
|
||||
max: 120
|
||||
allow-non-breakable-words: true
|
||||
allow-non-breakable-inline-mappings: false
|
||||
truthy:
|
||||
check-keys: false
|
||||
brackets:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 1
|
||||
|
||||
ignore: |
|
||||
vendor/
|
||||
40
CHANGELOG.md
Normal file
40
CHANGELOG.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- v3 API client (`Ivuorinen\BusinessDataFetcher\v3\Client`) with support for:
|
||||
- Company search (`searchCompanies()`)
|
||||
- Code list descriptions (`getDescription()`)
|
||||
- Postal code lookup (`getPostCodes()`)
|
||||
- Full company ZIP download (`getAllCompanies()`)
|
||||
- v3 DTOs based on the official OpenAPI schema
|
||||
- Pest test suite with 57 tests covering traits, DTOs, and both API clients
|
||||
- Shared HTTP infrastructure (`AbstractClient`, `HttpClientFactory`)
|
||||
- PHP 8.4 added to CI matrix
|
||||
- CI pipeline now runs linting, static analysis, and tests
|
||||
- `phpunit.xml` configuration
|
||||
- `.github/labels.yml` for label sync workflow
|
||||
|
||||
### Changed
|
||||
|
||||
- **BREAKING:** Replaced `spatie/data-transfer-object` with `cuyz/valinor` for DTO hydration
|
||||
- **BREAKING:** All v1 DTOs are now `final readonly` classes with constructor promotion
|
||||
- **BREAKING:** Removed `$sourceText` and `$authorityText` computed properties from traits; use `getSourceText()` and `getAuthorityString()` methods instead
|
||||
- **BREAKING:** `BusinessDataFetcher` constructor now accepts an optional `?Client` parameter for testability
|
||||
- Upgraded `squizlabs/php_codesniffer` from v3 to v4
|
||||
- **BREAKING:** `BusinessDataFetcher` now extends `AbstractClient`
|
||||
- **BREAKING:** Traits are now method-only (no properties or constructors)
|
||||
- **BREAKING:** Removed empty `HasVersion` trait (version is a regular constructor property)
|
||||
|
||||
### Removed
|
||||
|
||||
- `spatie/data-transfer-object` dependency
|
||||
- `ivuorinen/markdowndocs` dependency (incompatible with Pest's symfony/console requirement)
|
||||
- `composer docs` script (pending markdowndocs update)
|
||||
45
CLAUDE.md
Normal file
45
CLAUDE.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# CLAUDE.md
|
||||
|
||||
## Project Overview
|
||||
|
||||
PHP library for fetching Finnish business data from the PRH (Finnish Patent and Registration Office) open data API. Supports both v1 (BIS) and v3 (YTJ) APIs.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
composer lint # PSR-12 linting (phpcs v4) + Rector dry-run
|
||||
composer lint-fix # Auto-fix PSR-12 violations (phpcbf) + Rector apply
|
||||
composer rector # Run Rector standalone
|
||||
composer phpstan # Static analysis (level 9)
|
||||
composer test # Pest test suite
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
- **v1 Entry point**: `src/BusinessDataFetcher.php` — extends `AbstractClient`, fetches from `/bis/v1`
|
||||
- **v3 Entry point**: `src/v3/Client.php` — extends `AbstractClient`, fetches from `/opendata-ytj-api/v3`
|
||||
- **Shared HTTP**: `src/Http/AbstractClient.php` — base class with Guzzle + Valinor mapper
|
||||
- **v1 DTOs**: `src/v1/Dto/` — `final readonly` classes with Valinor-compatible constructors
|
||||
- **v3 DTOs**: `src/v3/Dto/` — `final readonly` classes matching the v3 OpenAPI schema
|
||||
- **Traits**: `src/v1/Traits/` — method-only helpers (`HasSource`, `HasLanguage`, `HasRegister`, `HasAuthority`, `HasChange`)
|
||||
- **Exceptions**: `src/v1/Exceptions/`, `src/v3/Exceptions/`
|
||||
- **Tests**: `tests/Unit/` — Pest tests for traits, DTOs, and both clients
|
||||
|
||||
## Conventions
|
||||
|
||||
- PHP 8.2+
|
||||
- PSR-12 coding standard (squizlabs/php_codesniffer v4)
|
||||
- Rector (`rector.php`): `deadCode`, `codeQuality`, `typeDeclarations` sets — no `codingStyle` (phpcs handles that)
|
||||
- PHPStan level 9
|
||||
- Valinor v1 for DTO hydration with `allowSuperfluousKeys()`
|
||||
- All DTOs are `final readonly` with constructor promotion
|
||||
- Traits are method-only (no properties)
|
||||
- Namespace: `Ivuorinen\BusinessDataFetcher`
|
||||
|
||||
## CI/CD
|
||||
|
||||
- GitHub Actions workflows in `.github/workflows/`
|
||||
- Reusable composite actions from `ivuorinen/actions` (not `ivuorinen/.github`)
|
||||
- Renovate config: `.github/renovate.json` extends `github>ivuorinen/renovate-config`
|
||||
- `pinact run -u -v --fix` — pin all action refs to commit SHAs after editing workflows
|
||||
- PHP matrix: 8.2, 8.3, 8.4
|
||||
47
CONTRIBUTING.md
Normal file
47
CONTRIBUTING.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Contributing
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 8.2+
|
||||
- Composer
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ivuorinen/business-data-fetcher.git
|
||||
cd business-data-fetcher
|
||||
composer install
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Running checks
|
||||
|
||||
```bash
|
||||
composer lint # PSR-12 linting
|
||||
composer lint-fix # Auto-fix PSR-12 violations
|
||||
composer phpstan # Static analysis (level 9)
|
||||
composer test # Pest test suite
|
||||
```
|
||||
|
||||
### Branch Strategy
|
||||
|
||||
- `main` — stable release branch
|
||||
- `feat/*` — feature branches
|
||||
- `fix/*` — bugfix branches
|
||||
|
||||
### Pull Request Process
|
||||
|
||||
1. Create a feature or fix branch from `main`
|
||||
2. Make your changes
|
||||
3. Ensure all checks pass: `composer lint && composer phpstan && composer test`
|
||||
4. Open a PR against `main`
|
||||
5. PRs require passing CI before merge
|
||||
|
||||
## Code Conventions
|
||||
|
||||
- PSR-12 coding standard
|
||||
- PHPStan level 9 strict analysis
|
||||
- All DTOs are `final readonly` classes with constructor promotion
|
||||
- Use Valinor for hydration (not manual array mapping)
|
||||
- Write Pest tests for new functionality
|
||||
111
README.md
111
README.md
@@ -1,36 +1,112 @@
|
||||
# Business Data Fetcher
|
||||
|
||||
This is an API client to Finnish Patent and Registration
|
||||
Office's (PRH) Business Information System (BIS) v1.
|
||||
[](https://github.com/ivuorinen/business-data-fetcher/actions/workflows/composer.yml)
|
||||
|
||||
Use it to get company data from the Business Information System by Business ID.
|
||||
PHP library for fetching Finnish business data from the PRH (Finnish Patent and Registration Office) open data API.
|
||||
|
||||
Supports both **v1** (BIS) and **v3** (YTJ) APIs.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
composer install ivuorinen/business-data-fetcher
|
||||
composer require ivuorinen/business-data-fetcher
|
||||
```
|
||||
|
||||
## Usage example
|
||||
## Usage
|
||||
|
||||
### v1 API (BIS)
|
||||
|
||||
Fetch company details by Business ID:
|
||||
|
||||
```php
|
||||
<?php
|
||||
require_once 'vendor/autoload.php';
|
||||
use Ivuorinen\BusinessDataFetcher\BusinessDataFetcher;
|
||||
|
||||
$client = new Ivuorinen\BusinessDataFetcher\BusinessDataFetcher();
|
||||
try {
|
||||
$results = $client->getBusinessInformation('1639413-9');
|
||||
print_r($results);
|
||||
} catch (\GuzzleHttp\Exception\GuzzleException $e) {
|
||||
var_dump($e);
|
||||
$client = new BusinessDataFetcher();
|
||||
$results = $client->getBusinessInformation('1639413-9');
|
||||
|
||||
foreach ($results as $company) {
|
||||
echo $company->name . "\n";
|
||||
echo $company->businessId . "\n";
|
||||
|
||||
foreach ($company->names as $name) {
|
||||
echo $name->name . ' (' . $name->getLanguageString() . ")\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Data source
|
||||
### v3 API (YTJ)
|
||||
|
||||
All models are transcribed from PRH Open Data portal. You can find the examples
|
||||
and models descriptions, among other details and live API query tool following
|
||||
this link: https://avoindata.prh.fi/ytj_en.html
|
||||
Search companies with multiple filters:
|
||||
|
||||
```php
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Client;
|
||||
|
||||
$client = new Client();
|
||||
|
||||
// Search by name
|
||||
$result = $client->searchCompanies(name: 'Example');
|
||||
echo "Found {$result->totalResults} companies\n";
|
||||
|
||||
foreach ($result->companies as $company) {
|
||||
echo $company->businessId->value . ' - ';
|
||||
echo $company->names[0]->name . "\n";
|
||||
}
|
||||
|
||||
// Search by Business ID
|
||||
$result = $client->searchCompanies(businessId: '1639413-9');
|
||||
|
||||
// Search with multiple filters
|
||||
$result = $client->searchCompanies(
|
||||
location: 'Helsinki',
|
||||
companyForm: 'OY',
|
||||
mainBusinessLine: '62010',
|
||||
);
|
||||
|
||||
// Get code list descriptions
|
||||
$description = $client->getDescription('REK', 'en');
|
||||
|
||||
// Get postal codes
|
||||
$postCodes = $client->getPostCodes('fi');
|
||||
|
||||
// Download all companies as ZIP
|
||||
$stream = $client->getAllCompanies();
|
||||
file_put_contents('companies.zip', $stream->getContents());
|
||||
```
|
||||
|
||||
## v1 vs v3 API Comparison
|
||||
|
||||
| Feature | v1 (BIS) | v3 (YTJ) |
|
||||
| --------- | ---------- | ---------- |
|
||||
| Base URL | `/bis/v1` | `/opendata-ytj-api/v3` |
|
||||
| Lookup | By Business ID only | Search by name, location, form, etc. |
|
||||
| Company form | String code | Structured with descriptions |
|
||||
| Addresses | Flat structure | Nested with PostOffice objects |
|
||||
| Code lists | N/A | `/description` endpoint |
|
||||
| Postal codes | N/A | `/post_codes` endpoint |
|
||||
| Bulk download | N/A | `/all_companies` ZIP |
|
||||
|
||||
## Migration from v1 DTOs
|
||||
|
||||
If upgrading from the Spatie DTO version:
|
||||
|
||||
- DTOs are now `final readonly` classes (no `->toArray()`)
|
||||
- `$sourceText` / `$authorityText` properties removed; use `getSourceText()` / `getAuthorityString()` methods
|
||||
- `HasVersion` trait removed; `$version` is a regular constructor property
|
||||
- `BusinessDataFetcher` constructor accepts an optional Guzzle `Client` for testing
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
composer lint # PSR-12 linting
|
||||
composer lint-fix # Auto-fix violations
|
||||
composer phpstan # Static analysis (level 9)
|
||||
composer test # Pest test suite
|
||||
```
|
||||
|
||||
## Data Source
|
||||
|
||||
- v1: <https://avoindata.prh.fi/ytj_en.html>
|
||||
- v3: <https://avoindata.prh.fi/fi/ytj/swagger-ui>
|
||||
|
||||
## Notice of Liability
|
||||
|
||||
@@ -40,4 +116,3 @@ of this library are providing this without compensation and cannot be held respo
|
||||
## License
|
||||
|
||||
[MIT licensed](LICENSE.md)
|
||||
|
||||
|
||||
69
captainhook.json
Normal file
69
captainhook.json
Normal file
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"pre-commit": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "\\CaptainHook\\App\\Hook\\Diff\\Action\\BlockSecrets",
|
||||
"options": {},
|
||||
"conditions": []
|
||||
},
|
||||
{
|
||||
"action": "composer lint-fix",
|
||||
"options": {},
|
||||
"conditions": []
|
||||
},
|
||||
{
|
||||
"action": "composer test",
|
||||
"options": {},
|
||||
"conditions": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"commit-msg": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "\\Ramsey\\CaptainHook\\ValidateConventionalCommit",
|
||||
"options": {},
|
||||
"conditions": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"pre-push": {
|
||||
"enabled": false,
|
||||
"actions": []
|
||||
},
|
||||
"post-commit": {
|
||||
"enabled": false,
|
||||
"actions": []
|
||||
},
|
||||
"post-merge": {
|
||||
"enabled": false,
|
||||
"actions": []
|
||||
},
|
||||
"post-checkout": {
|
||||
"enabled": false,
|
||||
"actions": []
|
||||
},
|
||||
"post-rewrite": {
|
||||
"enabled": false,
|
||||
"actions": []
|
||||
},
|
||||
"post-change": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer install",
|
||||
"options": {},
|
||||
"conditions": [
|
||||
{
|
||||
"exec": "CaptainHook.FileChanged.Any",
|
||||
"args": [
|
||||
["composer.json", "composer.lock"]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -5,23 +5,51 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"spatie/data-transfer-object": "^3.9"
|
||||
"cuyz/valinor": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ivuorinen\\BusinessDataFetcher\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Ivuorinen\\BusinessDataFetcher\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "^3.10",
|
||||
"ivuorinen/markdowndocs": "^4.0",
|
||||
"phpstan/phpstan": "^1.11"
|
||||
"squizlabs/php_codesniffer": "^4.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"pestphp/pest": "^3.0",
|
||||
"rector/rector": "^2.3",
|
||||
"captainhook/captainhook": "^5.0",
|
||||
"captainhook/hook-installer": "^1.0",
|
||||
"ramsey/conventional-commits": "^1.7",
|
||||
"captainhook/secrets": "^0.9.7",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"jbelien/phpstan-sarif-formatter": "^1.0",
|
||||
"bartlett/sarif-php-converters": "^1.0"
|
||||
},
|
||||
"scripts": {
|
||||
"docs": "php vendor/bin/phpdoc-md generate src > docs.md",
|
||||
"lint": "php vendor/bin/phpcs --standard=PSR12 src",
|
||||
"lint-fix": "php vendor/bin/phpcbf --standard=PSR12 src",
|
||||
"phpstan": "php vendor/bin/phpstan analyse"
|
||||
"lint": [
|
||||
"php vendor/bin/phpcs --standard=PSR12 src",
|
||||
"php vendor/bin/rector --dry-run"
|
||||
],
|
||||
"lint-fix": [
|
||||
"php vendor/bin/phpcbf --standard=PSR12 src",
|
||||
"php vendor/bin/rector"
|
||||
],
|
||||
"rector": "php vendor/bin/rector",
|
||||
"phpstan": "php vendor/bin/phpstan analyse",
|
||||
"test": "php vendor/bin/pest"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"captainhook/hook-installer": true,
|
||||
"phpstan/extension-installer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4837
composer.lock
generated
4837
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,8 @@ require_once "vendor/autoload.php";
|
||||
$client = new Ivuorinen\BusinessDataFetcher\BusinessDataFetcher();
|
||||
try {
|
||||
$results = $client->getBusinessInformation("1639413-9");
|
||||
print_r($results);
|
||||
// Convert to JSON
|
||||
echo json_encode($results, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT);
|
||||
} catch (Exception $e) {
|
||||
var_dump($e);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ BisCompanyForm {
|
||||
endDate (string, optional): Ending date of registration
|
||||
name (string): Name of company form
|
||||
language (string, optional): Two letter language code
|
||||
type (string): Type of company form
|
||||
type (string, optional): Type of company form
|
||||
}
|
||||
|
||||
BisCompanyLiquidation {
|
||||
|
||||
@@ -87,7 +87,7 @@ foreach ($classes as $className => $vars) {
|
||||
// Get name of the class from filename and split CamelCase to words.
|
||||
$classNameString = $className;
|
||||
$classNameString = str_replace("Bis", "", $classNameString);
|
||||
$classNameString = preg_replace('/(?<!^)[A-Z]/', ' $0', $classNameString);
|
||||
$classNameString = (string)preg_replace('/(?<!^)[A-Z]/', ' $0', $classNameString);
|
||||
$classNameString = ucwords($classNameString);
|
||||
|
||||
$usesHeader = [
|
||||
@@ -125,7 +125,7 @@ foreach ($classes as $className => $vars) {
|
||||
}
|
||||
|
||||
if (!empty($traits)) {
|
||||
$usesHeader[] = "use Ivuorinen\BusinessDataFetcher\Traits;";
|
||||
$usesHeader[] = "use Ivuorinen\BusinessDataFetcher\\v1\Traits;";
|
||||
}
|
||||
|
||||
$usesString = implode("\n", $usesHeader);
|
||||
@@ -134,7 +134,7 @@ foreach ($classes as $className => $vars) {
|
||||
|
||||
$file = "<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
namespace Ivuorinen\BusinessDataFetcher\\v1\Dto;
|
||||
$usesString
|
||||
|
||||
/**
|
||||
@@ -181,7 +181,9 @@ class $className extends DataTransferObject
|
||||
if (!empty($files)) {
|
||||
echo "Generating files:\n";
|
||||
|
||||
$dtoDir = sprintf('%s%s%s%s%s', dirname(__FILE__, 2), DS, 'src', DS, 'Dto');
|
||||
// Set the directory for the DTO classes.
|
||||
$dtoDir = implode(DS, [dirname(__FILE__, 2), 'src', 'v1', 'Dto']);
|
||||
|
||||
foreach ($files as $className => $file) {
|
||||
$filePath = sprintf('%s%s%s.php', $dtoDir, DS, $className);
|
||||
echo $filePath . "\n";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="PHP_CodeSniffer">
|
||||
<description>PSR12</description>
|
||||
<file>src</file>
|
||||
<rule ref="PSR12">
|
||||
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
|
||||
<exclude name="PSR12.Operators.OperatorSpacing"/>
|
||||
|
||||
18
phpunit.xml.dist
Normal file
18
phpunit.xml.dist
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
16
rector.php
Normal file
16
rector.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withPaths([
|
||||
__DIR__ . '/src',
|
||||
])
|
||||
->withPhpSets(php82: true)
|
||||
->withPreparedSets(
|
||||
deadCode: true,
|
||||
codeQuality: true,
|
||||
typeDeclarations: true,
|
||||
);
|
||||
@@ -2,47 +2,37 @@
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Ivuorinen\BusinessDataFetcher\Dto\BisCompanyDetails;
|
||||
use Ivuorinen\BusinessDataFetcher\Exceptions\ApiResponseErrorException;
|
||||
use Ivuorinen\BusinessDataFetcher\Http\AbstractClient;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyDetails;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Exceptions\ApiResponseErrorException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* Fetches and returns business data from avoindata
|
||||
*/
|
||||
class BusinessDataFetcher
|
||||
/** Client for the PRH BIS v1 API (Finnish business data). */
|
||||
class BusinessDataFetcher extends AbstractClient
|
||||
{
|
||||
/**
|
||||
* @var \GuzzleHttp\Client
|
||||
*/
|
||||
private Client $httpClient;
|
||||
|
||||
/**
|
||||
* BusinessDataFetcher constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
/** @inheritDoc */
|
||||
protected function getBaseUri(): string
|
||||
{
|
||||
$this->httpClient = new Client([
|
||||
'base_uri' => 'https://avoindata.prh.fi',
|
||||
'timeout' => 2,
|
||||
]);
|
||||
return 'https://avoindata.prh.fi';
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function getTimeout(): int
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch Business Information.
|
||||
*
|
||||
* @return BisCompanyDetails[] $response_data
|
||||
* @return BisCompanyDetails[]
|
||||
* @throws \Exception|\GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function getBusinessInformation(string $businessId): array
|
||||
{
|
||||
// Set request variables
|
||||
$requestUrl = '/bis/v1';
|
||||
|
||||
// Get the business data
|
||||
try {
|
||||
$uri = $requestUrl . '/' . $businessId;
|
||||
$uri = '/bis/v1/' . rawurlencode($businessId);
|
||||
$response = $this->httpClient->get($uri);
|
||||
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
@@ -52,7 +42,7 @@ class BusinessDataFetcher
|
||||
);
|
||||
}
|
||||
|
||||
$response_data = $this->parseResponse($response);
|
||||
return $this->parseResponse($response);
|
||||
} catch (RequestException $exception) {
|
||||
throw new ApiResponseErrorException(
|
||||
$exception->getMessage(),
|
||||
@@ -60,8 +50,6 @@ class BusinessDataFetcher
|
||||
$exception
|
||||
);
|
||||
}
|
||||
|
||||
return $response_data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,8 +57,7 @@ class BusinessDataFetcher
|
||||
*
|
||||
* @return BisCompanyDetails[]
|
||||
* @throws \JsonException
|
||||
* @throws \Spatie\DataTransferObject\Exceptions\UnknownProperties
|
||||
* @throws \Ivuorinen\BusinessDataFetcher\Exceptions\ApiResponseErrorException
|
||||
* @throws ApiResponseErrorException
|
||||
*/
|
||||
public function parseResponse(ResponseInterface $response): array
|
||||
{
|
||||
@@ -88,7 +75,7 @@ class BusinessDataFetcher
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($data['results'])) {
|
||||
if (!isset($data['results']) || !is_array($data['results'])) {
|
||||
throw new ApiResponseErrorException(
|
||||
'Invalid response data',
|
||||
$response->getStatusCode()
|
||||
@@ -98,7 +85,7 @@ class BusinessDataFetcher
|
||||
$results = [];
|
||||
|
||||
foreach ($data['results'] as $result) {
|
||||
$results[] = new BisCompanyDetails($result);
|
||||
$results[] = $this->mapper->map(BisCompanyDetails::class, $result);
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Address
|
||||
*/
|
||||
class BisAddress extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Care of address
|
||||
*/
|
||||
public ?string $careOf = null;
|
||||
|
||||
/**
|
||||
* Street address
|
||||
*/
|
||||
public ?string $street = null;
|
||||
|
||||
/**
|
||||
* ZIP code
|
||||
*/
|
||||
public ?string $postCode = null;
|
||||
|
||||
/**
|
||||
* City of address
|
||||
*/
|
||||
public ?string $city = null;
|
||||
|
||||
/**
|
||||
* Type of address, 1 for street address, 2 for postal address
|
||||
*/
|
||||
public int $type;
|
||||
|
||||
/**
|
||||
* Two letter country code
|
||||
*/
|
||||
public ?string $country = null;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Business Id Change
|
||||
*/
|
||||
class BisCompanyBusinessIdChange extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
use Traits\HasChange;
|
||||
|
||||
/**
|
||||
* Description of reason
|
||||
*/
|
||||
public string $description = '';
|
||||
|
||||
/**
|
||||
* Reason code
|
||||
*/
|
||||
public string $reason = '';
|
||||
|
||||
/**
|
||||
* Date of Business ID change
|
||||
*/
|
||||
public ?string $changeDate = null;
|
||||
|
||||
/**
|
||||
* Old Business ID
|
||||
*/
|
||||
public string $oldBusinessId = '';
|
||||
|
||||
/**
|
||||
* New Business ID
|
||||
*/
|
||||
public string $newBusinessId = '';
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Business Line
|
||||
*/
|
||||
class BisCompanyBusinessLine extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Zero for main line of business, positive for others
|
||||
*/
|
||||
public int $order;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Name of line of business
|
||||
*/
|
||||
public string $name = '';
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Contact Detail
|
||||
*/
|
||||
class BisCompanyContactDetail extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Value of contact detail
|
||||
*/
|
||||
public string $value = '';
|
||||
|
||||
/**
|
||||
* Type of contact detail
|
||||
*/
|
||||
public string $type = '';
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Spatie\DataTransferObject\Attributes\CastWith;
|
||||
use Spatie\DataTransferObject\Casters;
|
||||
|
||||
/**
|
||||
* Company Details
|
||||
*/
|
||||
class BisCompanyDetails extends DataTransferObject
|
||||
{
|
||||
/**
|
||||
* Primary company name and translations
|
||||
* @var BisCompanyName[] $names
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyName::class)]
|
||||
public array $names = [];
|
||||
|
||||
/**
|
||||
* Auxiliary company name and translations
|
||||
* @var ?BisCompanyName[] $auxiliaryNames
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyName::class)]
|
||||
public ?array $auxiliaryNames = [];
|
||||
|
||||
/**
|
||||
* Company's street and postal addresses
|
||||
* @var ?BisAddress[] $addresses
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisAddress::class)]
|
||||
public ?array $addresses = [];
|
||||
|
||||
/**
|
||||
* Company form and translations
|
||||
* @var ?BisCompanyForm[] $companyForms
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyForm::class)]
|
||||
public ?array $companyForms = [];
|
||||
|
||||
/**
|
||||
* Bankruptcy, liquidation or restructuring proceedings
|
||||
* @var ?BisCompanyLiquidation[] $liquidations
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyLiquidation::class)]
|
||||
public ?array $liquidations = [];
|
||||
|
||||
/**
|
||||
* Company's lines of business and translations
|
||||
* @var ?BisCompanyBusinessLine[] $businessLines
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyBusinessLine::class)]
|
||||
public ?array $businessLines = [];
|
||||
|
||||
/**
|
||||
* Company's language(s)
|
||||
* @var ?BisCompanyLanguage[] $languages
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyLanguage::class)]
|
||||
public ?array $languages = [];
|
||||
|
||||
/**
|
||||
* Company's place of registered office and its translations
|
||||
* @var ?BisCompanyRegisteredOffice[] $registeredOffices
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyRegisteredOffice::class)]
|
||||
public ?array $registeredOffices = [];
|
||||
|
||||
/**
|
||||
* Company's contact details and translations
|
||||
* @var ?BisCompanyContactDetail[] $contactDetails
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyContactDetail::class)]
|
||||
public ?array $contactDetails = [];
|
||||
|
||||
/**
|
||||
* Company's registered entries
|
||||
* @var ?BisCompanyRegisteredEntry[] $registeredEntries
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyRegisteredEntry::class)]
|
||||
public ?array $registeredEntries = [];
|
||||
|
||||
/**
|
||||
* Company's Business ID changes
|
||||
* @var ?BisCompanyBusinessIdChange[] $businessIdChanges
|
||||
*/
|
||||
#[CastWith(Casters\ArrayCaster::class, itemType: BisCompanyBusinessIdChange::class)]
|
||||
public ?array $businessIdChanges = [];
|
||||
|
||||
/**
|
||||
* Business ID
|
||||
*/
|
||||
public string $businessId = '';
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Company form
|
||||
*/
|
||||
public ?string $companyForm = null;
|
||||
|
||||
/**
|
||||
* A URI for more details, if details aren't already included
|
||||
*/
|
||||
public ?string $detailsUri = null;
|
||||
|
||||
/**
|
||||
* Primary company name
|
||||
*/
|
||||
public string $name = '';
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Form
|
||||
*/
|
||||
class BisCompanyForm extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Name of company form
|
||||
*/
|
||||
public string $name = '';
|
||||
|
||||
/**
|
||||
* Type of company form
|
||||
*/
|
||||
public string $type = '';
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Language
|
||||
*/
|
||||
class BisCompanyLanguage extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Name of language
|
||||
*/
|
||||
public string $name = '';
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Liquidation
|
||||
*/
|
||||
class BisCompanyLiquidation extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Bankruptcy, liquidation or restructuring proceedings
|
||||
*/
|
||||
public string $name = '';
|
||||
|
||||
/**
|
||||
* Type of liquidation
|
||||
*/
|
||||
public string $type = '';
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Name
|
||||
*/
|
||||
class BisCompanyName extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Zero for primary company name, other for translations of the primary company name and auxiliary company names
|
||||
*/
|
||||
public int $order;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Company name
|
||||
*/
|
||||
public string $name = '';
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Registered Entry
|
||||
*/
|
||||
class BisCompanyRegisteredEntry extends DataTransferObject
|
||||
{
|
||||
use Traits\HasAuthority;
|
||||
use Traits\HasLanguage;
|
||||
use Traits\HasRegister;
|
||||
|
||||
/**
|
||||
* Description of entry
|
||||
*/
|
||||
public string $description = '';
|
||||
|
||||
/**
|
||||
* Zero for common entries, one for ‘Unregistered’ and two for ‘Registered’
|
||||
*/
|
||||
public int $status;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Dto;
|
||||
|
||||
use Spatie\DataTransferObject\DataTransferObject;
|
||||
use Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
/**
|
||||
* Company Registered Office
|
||||
*/
|
||||
class BisCompanyRegisteredOffice extends DataTransferObject
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasVersion;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
/**
|
||||
* Zero for primary place of registered office, positive for others
|
||||
*/
|
||||
public int $order;
|
||||
|
||||
/**
|
||||
* Date of registration
|
||||
*/
|
||||
public string $registrationDate = '';
|
||||
|
||||
/**
|
||||
* Ending date of registration
|
||||
*/
|
||||
public ?string $endDate = null;
|
||||
|
||||
/**
|
||||
* Name of place of registered office
|
||||
*/
|
||||
public string $name = '';
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ApiResponseErrorException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnexpectedValueException extends Exception
|
||||
{
|
||||
}
|
||||
73
src/Http/AbstractClient.php
Normal file
73
src/Http/AbstractClient.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Http;
|
||||
|
||||
use CuyZ\Valinor\Mapper\TreeMapper;
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* Base HTTP client for PRH API communication.
|
||||
*
|
||||
* Provides shared Guzzle HTTP client and Valinor mapper instances
|
||||
* for concrete API client implementations.
|
||||
*/
|
||||
abstract class AbstractClient
|
||||
{
|
||||
protected Client $httpClient;
|
||||
|
||||
protected TreeMapper $mapper;
|
||||
|
||||
/** Initialize HTTP client and Valinor mapper. */
|
||||
public function __construct(?Client $httpClient = null)
|
||||
{
|
||||
$this->httpClient = $httpClient ?? HttpClientFactory::create(
|
||||
$this->getBaseUri(),
|
||||
$this->getTimeout()
|
||||
);
|
||||
|
||||
$this->mapper = (new MapperBuilder())
|
||||
->allowSuperfluousKeys()
|
||||
->mapper();
|
||||
}
|
||||
|
||||
/** Get the base URI for the API. */
|
||||
abstract protected function getBaseUri(): string;
|
||||
|
||||
/** Get the HTTP request timeout in seconds. */
|
||||
protected function getTimeout(): int
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a GET request and decode the JSON response.
|
||||
*
|
||||
* @param array<string, mixed> $query
|
||||
* @return array<mixed>
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
protected function getJson(string $uri, array $query = []): array
|
||||
{
|
||||
$options = [];
|
||||
if ($query !== []) {
|
||||
$options['query'] = $query;
|
||||
}
|
||||
|
||||
$response = $this->httpClient->get($uri, $options);
|
||||
|
||||
$data = json_decode(
|
||||
$response->getBody()->getContents(),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
|
||||
if (!is_array($data)) {
|
||||
throw new \JsonException('Response is not a valid JSON object or array');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
18
src/Http/HttpClientFactory.php
Normal file
18
src/Http/HttpClientFactory.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Http;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/** Factory for creating pre-configured Guzzle HTTP clients. */
|
||||
class HttpClientFactory
|
||||
{
|
||||
/** Create a Guzzle client with the given base URI and timeout. */
|
||||
public static function create(string $baseUri, int $timeout = 10): Client
|
||||
{
|
||||
return new Client([
|
||||
'base_uri' => $baseUri,
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
trait HasLanguage
|
||||
{
|
||||
/**
|
||||
* @see getLanguageString()
|
||||
* @var string|null $language Two letter language code
|
||||
* (e.g. 'fi', 'sv', 'en')
|
||||
*/
|
||||
public ?string $language;
|
||||
|
||||
/**
|
||||
* Get the language code as a string.
|
||||
*/
|
||||
public function getLanguageString(): string
|
||||
{
|
||||
return match ($this->language) {
|
||||
'fi' => 'finnish',
|
||||
'en' => 'english',
|
||||
'sv' => 'swedish',
|
||||
default => 'unknown:' . $this->language,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
trait HasSource
|
||||
{
|
||||
/**
|
||||
* Source of the information.
|
||||
*
|
||||
* source (integer, optional):
|
||||
* - Zero for common,
|
||||
* - one for Finnish Patent and Registration Office,
|
||||
* - two for Tax Administration or
|
||||
* - three for Business Information System
|
||||
*
|
||||
* Use `getSourceText()` to get the text representation.
|
||||
*
|
||||
* @see getSourceText()
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $source;
|
||||
|
||||
public function getSourceText(): string
|
||||
{
|
||||
return match ($this->source) {
|
||||
0 => 'common',
|
||||
1 => 'Finnish Patent and Registration Office',
|
||||
2 => 'Tax Administration',
|
||||
3 => 'Business Information System',
|
||||
default => '',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Traits;
|
||||
|
||||
trait HasVersion
|
||||
{
|
||||
/**
|
||||
* One for current version and >1 for historical contact details.
|
||||
*/
|
||||
public int $version = 0;
|
||||
}
|
||||
27
src/v1/Dto/BisAddress.php
Normal file
27
src/v1/Dto/BisAddress.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a postal or visiting address from the BIS v1 API. */
|
||||
final readonly class BisAddress
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public int $type = 0,
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public ?string $careOf = null,
|
||||
public ?string $street = null,
|
||||
public ?string $postCode = null,
|
||||
public ?string $city = null,
|
||||
public ?string $country = null,
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
25
src/v1/Dto/BisCompanyBusinessIdChange.php
Normal file
25
src/v1/Dto/BisCompanyBusinessIdChange.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a business ID change event (e.g. fusion, division). */
|
||||
final readonly class BisCompanyBusinessIdChange
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
use Traits\HasChange;
|
||||
|
||||
public function __construct(
|
||||
public string $description = '',
|
||||
public string $reason = '',
|
||||
public ?string $changeDate = null,
|
||||
public string $oldBusinessId = '',
|
||||
public string $newBusinessId = '',
|
||||
public ?int $source = null,
|
||||
public ?string $language = null,
|
||||
public string|int|null $change = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
23
src/v1/Dto/BisCompanyBusinessLine.php
Normal file
23
src/v1/Dto/BisCompanyBusinessLine.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company's line of business (industry classification). */
|
||||
final readonly class BisCompanyBusinessLine
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public int $order = 0,
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $name = '',
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
23
src/v1/Dto/BisCompanyContactDetail.php
Normal file
23
src/v1/Dto/BisCompanyContactDetail.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company contact detail (phone, email, website, etc.). */
|
||||
final readonly class BisCompanyContactDetail
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $value = '',
|
||||
public string $type = '',
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
40
src/v1/Dto/BisCompanyDetails.php
Normal file
40
src/v1/Dto/BisCompanyDetails.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
/** Top-level DTO for a company record from the BIS v1 API. */
|
||||
final readonly class BisCompanyDetails
|
||||
{
|
||||
/**
|
||||
* @param list<BisCompanyName> $names
|
||||
* @param list<BisCompanyName> $auxiliaryNames
|
||||
* @param list<BisAddress> $addresses
|
||||
* @param list<BisCompanyForm> $companyForms
|
||||
* @param list<BisCompanyLiquidation> $liquidations
|
||||
* @param list<BisCompanyBusinessLine> $businessLines
|
||||
* @param list<BisCompanyLanguage> $languages
|
||||
* @param list<BisCompanyRegisteredOffice> $registeredOffices
|
||||
* @param list<BisCompanyContactDetail> $contactDetails
|
||||
* @param list<BisCompanyRegisteredEntry> $registeredEntries
|
||||
* @param list<BisCompanyBusinessIdChange> $businessIdChanges
|
||||
*/
|
||||
public function __construct(
|
||||
public string $businessId = '',
|
||||
public string $registrationDate = '',
|
||||
public ?string $companyForm = null,
|
||||
public ?string $detailsUri = null,
|
||||
public string $name = '',
|
||||
public array $names = [],
|
||||
public array $auxiliaryNames = [],
|
||||
public array $addresses = [],
|
||||
public array $companyForms = [],
|
||||
public array $liquidations = [],
|
||||
public array $businessLines = [],
|
||||
public array $languages = [],
|
||||
public array $registeredOffices = [],
|
||||
public array $contactDetails = [],
|
||||
public array $registeredEntries = [],
|
||||
public array $businessIdChanges = [],
|
||||
) {
|
||||
}
|
||||
}
|
||||
23
src/v1/Dto/BisCompanyForm.php
Normal file
23
src/v1/Dto/BisCompanyForm.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company's legal form (e.g. Ltd, cooperative). */
|
||||
final readonly class BisCompanyForm
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $name = '',
|
||||
public ?string $type = null,
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
22
src/v1/Dto/BisCompanyLanguage.php
Normal file
22
src/v1/Dto/BisCompanyLanguage.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company's registered language. */
|
||||
final readonly class BisCompanyLanguage
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $name = '',
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
23
src/v1/Dto/BisCompanyLiquidation.php
Normal file
23
src/v1/Dto/BisCompanyLiquidation.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company liquidation or bankruptcy entry. */
|
||||
final readonly class BisCompanyLiquidation
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $name = '',
|
||||
public string $type = '',
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
23
src/v1/Dto/BisCompanyName.php
Normal file
23
src/v1/Dto/BisCompanyName.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company name or auxiliary name entry. */
|
||||
final readonly class BisCompanyName
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public int $order = 0,
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $name = '',
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
24
src/v1/Dto/BisCompanyRegisteredEntry.php
Normal file
24
src/v1/Dto/BisCompanyRegisteredEntry.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a registration entry in a public register. */
|
||||
final readonly class BisCompanyRegisteredEntry
|
||||
{
|
||||
use Traits\HasAuthority;
|
||||
use Traits\HasLanguage;
|
||||
use Traits\HasRegister;
|
||||
|
||||
public function __construct(
|
||||
public string $description = '',
|
||||
public int $status = 0,
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public int $authority = 0,
|
||||
public ?string $language = null,
|
||||
public int|null $register = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
23
src/v1/Dto/BisCompanyRegisteredOffice.php
Normal file
23
src/v1/Dto/BisCompanyRegisteredOffice.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Dto;
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Represents a company's registered office (domicile). */
|
||||
final readonly class BisCompanyRegisteredOffice
|
||||
{
|
||||
use Traits\HasSource;
|
||||
use Traits\HasLanguage;
|
||||
|
||||
public function __construct(
|
||||
public int $order = 0,
|
||||
public string $registrationDate = '',
|
||||
public ?string $endDate = null,
|
||||
public string $name = '',
|
||||
public ?int $source = null,
|
||||
public int $version = 0,
|
||||
public ?string $language = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
10
src/v1/Exceptions/ApiResponseErrorException.php
Normal file
10
src/v1/Exceptions/ApiResponseErrorException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/** Thrown when the BIS v1 API returns an error response. */
|
||||
class ApiResponseErrorException extends Exception
|
||||
{
|
||||
}
|
||||
10
src/v1/Exceptions/UnexpectedValueException.php
Normal file
10
src/v1/Exceptions/UnexpectedValueException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/** Thrown when the BIS v1 API returns an unexpected value. */
|
||||
class UnexpectedValueException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -1,18 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Traits;
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Provides a human-readable authority name from the authority code. */
|
||||
trait HasAuthority
|
||||
{
|
||||
/**
|
||||
* @see getChangeString()
|
||||
* @var int $authority What authority the change is related to.
|
||||
*/
|
||||
public int $authority;
|
||||
|
||||
/**
|
||||
* Get the name of the authority.
|
||||
*/
|
||||
/** Get the authority name (e.g. "Tax Administration"). */
|
||||
public function getAuthorityString(): string
|
||||
{
|
||||
return match ($this->authority) {
|
||||
@@ -1,19 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Traits;
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Provides a human-readable description of a business ID change reason. */
|
||||
trait HasChange
|
||||
{
|
||||
/**
|
||||
* @see getChangeString()
|
||||
* @var string|int|null $change Change as a string or integer.
|
||||
* Models claim this is an integer, but it can also be a string.
|
||||
*/
|
||||
public string|int|null $change;
|
||||
|
||||
/**
|
||||
* Get the description string of the change.
|
||||
*/
|
||||
/** Get the change reason description (e.g. "Fusion", "Division"). */
|
||||
public function getChangeString(): string
|
||||
{
|
||||
return match ($this->change) {
|
||||
22
src/v1/Traits/HasLanguage.php
Normal file
22
src/v1/Traits/HasLanguage.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Provides a human-readable language name from a language code. */
|
||||
trait HasLanguage
|
||||
{
|
||||
/** Get the language name (e.g. "finnish", "english", "swedish"). */
|
||||
public function getLanguageString(): string
|
||||
{
|
||||
if ($this->language === null) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
return match ($this->language) {
|
||||
'fi' => 'finnish',
|
||||
'en' => 'english',
|
||||
'sv' => 'swedish',
|
||||
default => 'unknown:' . $this->language,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\Traits;
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Provides a human-readable register name from the register code. */
|
||||
trait HasRegister
|
||||
{
|
||||
/**
|
||||
* @see getRegisterString()
|
||||
* @var int|null $register What register the change is related to.
|
||||
*/
|
||||
public int|null $register;
|
||||
|
||||
/**
|
||||
* Get the name of the register.
|
||||
*/
|
||||
/** Get the register name (e.g. "Trade Register", "VAT Register"). */
|
||||
public function getRegisterString(): string
|
||||
{
|
||||
return match ($this->register) {
|
||||
19
src/v1/Traits/HasSource.php
Normal file
19
src/v1/Traits/HasSource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v1\Traits;
|
||||
|
||||
/** Provides a human-readable data source name from the source code. */
|
||||
trait HasSource
|
||||
{
|
||||
/** Get the data source name (e.g. "Tax Administration", "PRH"). */
|
||||
public function getSourceText(): string
|
||||
{
|
||||
return match ($this->source) {
|
||||
0 => 'common',
|
||||
1 => 'Finnish Patent and Registration Office',
|
||||
2 => 'Tax Administration',
|
||||
3 => 'Business Information System',
|
||||
default => '',
|
||||
};
|
||||
}
|
||||
}
|
||||
124
src/v3/Client.php
Normal file
124
src/v3/Client.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Ivuorinen\BusinessDataFetcher\Http\AbstractClient;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Dto\CompanySearchResult;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Dto\PostCodeEntry;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Exceptions\V3ApiException;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
/** Client for the PRH YTJ v3 API (Finnish business data). */
|
||||
class Client extends AbstractClient
|
||||
{
|
||||
private const API_PREFIX = '/opendata-ytj-api/v3';
|
||||
|
||||
/** @inheritDoc */
|
||||
protected function getBaseUri(): string
|
||||
{
|
||||
return 'https://avoindata.prh.fi';
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for companies.
|
||||
*
|
||||
* @throws V3ApiException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function searchCompanies(
|
||||
?string $name = null,
|
||||
?string $businessId = null,
|
||||
?string $location = null,
|
||||
?string $companyForm = null,
|
||||
?string $mainBusinessLine = null,
|
||||
?string $registrationDateStart = null,
|
||||
?string $registrationDateEnd = null,
|
||||
?string $postCode = null,
|
||||
?string $businessIdRegistrationStart = null,
|
||||
?string $businessIdRegistrationEnd = null,
|
||||
?int $page = null,
|
||||
): CompanySearchResult {
|
||||
$query = array_filter([
|
||||
'name' => $name,
|
||||
'businessId' => $businessId,
|
||||
'location' => $location,
|
||||
'companyForm' => $companyForm,
|
||||
'mainBusinessLine' => $mainBusinessLine,
|
||||
'registrationDateStart' => $registrationDateStart,
|
||||
'registrationDateEnd' => $registrationDateEnd,
|
||||
'postCode' => $postCode,
|
||||
'businessIdRegistrationStart' => $businessIdRegistrationStart,
|
||||
'businessIdRegistrationEnd' => $businessIdRegistrationEnd,
|
||||
'page' => $page,
|
||||
], fn (string|int|null $v): bool => $v !== null);
|
||||
|
||||
try {
|
||||
$data = $this->getJson(self::API_PREFIX . '/companies', $query);
|
||||
return $this->mapper->map(CompanySearchResult::class, $data);
|
||||
} catch (RequestException | \JsonException $e) {
|
||||
throw new V3ApiException($e->getMessage(), (int) $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve code list description.
|
||||
*
|
||||
* @throws V3ApiException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function getDescription(string $code, string $lang = 'en'): string
|
||||
{
|
||||
try {
|
||||
$response = $this->httpClient->get(self::API_PREFIX . '/description', [
|
||||
'query' => ['code' => $code, 'lang' => $lang],
|
||||
]);
|
||||
|
||||
return $response->getBody()->getContents();
|
||||
} catch (RequestException $e) {
|
||||
throw new V3ApiException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve postal code details.
|
||||
*
|
||||
* @return PostCodeEntry[]
|
||||
* @throws V3ApiException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function getPostCodes(string $lang = 'en'): array
|
||||
{
|
||||
try {
|
||||
$data = $this->getJson(self::API_PREFIX . '/post_codes', ['lang' => $lang]);
|
||||
|
||||
$results = [];
|
||||
foreach ($data as $entry) {
|
||||
$results[] = $this->mapper->map(PostCodeEntry::class, $entry);
|
||||
}
|
||||
|
||||
return $results;
|
||||
} catch (RequestException | \JsonException $e) {
|
||||
throw new V3ApiException($e->getMessage(), (int) $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all companies as a ZIP download stream.
|
||||
*
|
||||
* @throws V3ApiException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function getAllCompanies(): StreamInterface
|
||||
{
|
||||
try {
|
||||
$response = $this->httpClient->get(self::API_PREFIX . '/all_companies', [
|
||||
'stream' => true,
|
||||
]);
|
||||
|
||||
return $response->getBody();
|
||||
} catch (RequestException $e) {
|
||||
throw new V3ApiException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/v3/Dto/Address.php
Normal file
28
src/v3/Dto/Address.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a postal or visiting address from the YTJ v3 API. */
|
||||
final readonly class Address
|
||||
{
|
||||
/**
|
||||
* @param list<PostOffice> $postOffices
|
||||
*/
|
||||
public function __construct(
|
||||
public int $type = 0,
|
||||
public string $source = '',
|
||||
public ?string $street = null,
|
||||
public ?string $postCode = null,
|
||||
public array $postOffices = [],
|
||||
public ?string $postOfficeBox = null,
|
||||
public ?string $buildingNumber = null,
|
||||
public ?string $entrance = null,
|
||||
public ?string $apartmentNumber = null,
|
||||
public ?string $apartmentIdSuffix = null,
|
||||
public ?string $co = null,
|
||||
public ?string $country = null,
|
||||
public ?string $freeAddressLine = null,
|
||||
public ?string $registrationDate = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
14
src/v3/Dto/BusinessId.php
Normal file
14
src/v3/Dto/BusinessId.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a Finnish business ID (Y-tunnus) with registration metadata. */
|
||||
final readonly class BusinessId
|
||||
{
|
||||
public function __construct(
|
||||
public string $value,
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $source = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
19
src/v3/Dto/BusinessLine.php
Normal file
19
src/v3/Dto/BusinessLine.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a company's line of business (industry classification) from the YTJ v3 API. */
|
||||
final readonly class BusinessLine
|
||||
{
|
||||
/**
|
||||
* @param list<DescriptionEntry> $descriptions
|
||||
*/
|
||||
public function __construct(
|
||||
public string $code,
|
||||
public array $descriptions = [],
|
||||
public ?string $typeCodeSet = null,
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $source = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
32
src/v3/Dto/Company.php
Normal file
32
src/v3/Dto/Company.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Top-level DTO for a company record from the YTJ v3 API. */
|
||||
final readonly class Company
|
||||
{
|
||||
/**
|
||||
* @param list<RegisterName> $names
|
||||
* @param list<CompanyForm> $companyForms
|
||||
* @param list<CompanySituation> $companySituations
|
||||
* @param list<RegisteredEntry> $registeredEntries
|
||||
* @param list<Address> $addresses
|
||||
*/
|
||||
public function __construct(
|
||||
public BusinessId $businessId,
|
||||
public string $tradeRegisterStatus = '',
|
||||
public string $lastModified = '',
|
||||
public ?EuId $euId = null,
|
||||
public array $names = [],
|
||||
public ?BusinessLine $mainBusinessLine = null,
|
||||
public ?Website $website = null,
|
||||
public array $companyForms = [],
|
||||
public array $companySituations = [],
|
||||
public array $registeredEntries = [],
|
||||
public array $addresses = [],
|
||||
public ?string $status = null,
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $endDate = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
20
src/v3/Dto/CompanyForm.php
Normal file
20
src/v3/Dto/CompanyForm.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a company's legal form (e.g. Ltd, cooperative) from the YTJ v3 API. */
|
||||
final readonly class CompanyForm
|
||||
{
|
||||
/**
|
||||
* @param list<DescriptionEntry> $descriptions
|
||||
*/
|
||||
public function __construct(
|
||||
public string $type = '',
|
||||
public string $source = '',
|
||||
public int $version = 0,
|
||||
public array $descriptions = [],
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $endDate = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
16
src/v3/Dto/CompanySearchResult.php
Normal file
16
src/v3/Dto/CompanySearchResult.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Paginated search result containing matched companies from the YTJ v3 API. */
|
||||
final readonly class CompanySearchResult
|
||||
{
|
||||
/**
|
||||
* @param list<Company> $companies
|
||||
*/
|
||||
public function __construct(
|
||||
public int $totalResults = 0,
|
||||
public array $companies = [],
|
||||
) {
|
||||
}
|
||||
}
|
||||
15
src/v3/Dto/CompanySituation.php
Normal file
15
src/v3/Dto/CompanySituation.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a company's operational situation (e.g. active, dissolved). */
|
||||
final readonly class CompanySituation
|
||||
{
|
||||
public function __construct(
|
||||
public string $type,
|
||||
public string $source,
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $endDate = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
13
src/v3/Dto/DescriptionEntry.php
Normal file
13
src/v3/Dto/DescriptionEntry.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a localized description with a language code. */
|
||||
final readonly class DescriptionEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $languageCode = '',
|
||||
public string $description = '',
|
||||
) {
|
||||
}
|
||||
}
|
||||
13
src/v3/Dto/EuId.php
Normal file
13
src/v3/Dto/EuId.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a company's EU VAT identification number. */
|
||||
final readonly class EuId
|
||||
{
|
||||
public function __construct(
|
||||
public string $value = '',
|
||||
public ?string $source = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
16
src/v3/Dto/PostCodeEntry.php
Normal file
16
src/v3/Dto/PostCodeEntry.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a Finnish postal code with associated city and municipality. */
|
||||
final readonly class PostCodeEntry
|
||||
{
|
||||
public function __construct(
|
||||
public string $postCode = '',
|
||||
public string $city = '',
|
||||
public bool $active = true,
|
||||
public string $languageCode = '',
|
||||
public ?string $municipalityCode = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
14
src/v3/Dto/PostOffice.php
Normal file
14
src/v3/Dto/PostOffice.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a localized post office name within an address. */
|
||||
final readonly class PostOffice
|
||||
{
|
||||
public function __construct(
|
||||
public string $city,
|
||||
public string $languageCode,
|
||||
public ?string $municipalityCode = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
17
src/v3/Dto/RegisterName.php
Normal file
17
src/v3/Dto/RegisterName.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a company name registered in a specific register. */
|
||||
final readonly class RegisterName
|
||||
{
|
||||
public function __construct(
|
||||
public string $name = '',
|
||||
public string $type = '',
|
||||
public string $source = '',
|
||||
public int $version = 0,
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $endDate = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
20
src/v3/Dto/RegisteredEntry.php
Normal file
20
src/v3/Dto/RegisteredEntry.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a registration entry in a public register from the YTJ v3 API. */
|
||||
final readonly class RegisteredEntry
|
||||
{
|
||||
/**
|
||||
* @param list<DescriptionEntry> $descriptions
|
||||
*/
|
||||
public function __construct(
|
||||
public string $type = '',
|
||||
public string $register = '',
|
||||
public string $authority = '',
|
||||
public array $descriptions = [],
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $endDate = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
14
src/v3/Dto/Website.php
Normal file
14
src/v3/Dto/Website.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Dto;
|
||||
|
||||
/** Represents a company's registered website URL. */
|
||||
final readonly class Website
|
||||
{
|
||||
public function __construct(
|
||||
public string $url,
|
||||
public ?string $registrationDate = null,
|
||||
public ?string $source = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
10
src/v3/Exceptions/V3ApiException.php
Normal file
10
src/v3/Exceptions/V3ApiException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Ivuorinen\BusinessDataFetcher\v3\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/** Thrown when the YTJ v3 API returns an error response. */
|
||||
class V3ApiException extends Exception
|
||||
{
|
||||
}
|
||||
112
tests/Fixtures/sample-response.json
Normal file
112
tests/Fixtures/sample-response.json
Normal file
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"type": "fi.prh.opendata.bis",
|
||||
"version": "1",
|
||||
"totalResults": -1,
|
||||
"resultsFrom": 0,
|
||||
"previousResultsUri": null,
|
||||
"nextResultsUri": null,
|
||||
"exceptionNoticeUri": null,
|
||||
"results": [
|
||||
{
|
||||
"businessId": "1639413-9",
|
||||
"registrationDate": "2001-04-03",
|
||||
"companyForm": "OY",
|
||||
"detailsUri": null,
|
||||
"name": "Oy Example Ab",
|
||||
"names": [
|
||||
{
|
||||
"order": 0,
|
||||
"version": 1,
|
||||
"registrationDate": "2015-01-19",
|
||||
"endDate": null,
|
||||
"name": "Oy Example Ab",
|
||||
"source": 0,
|
||||
"language": "fi"
|
||||
}
|
||||
],
|
||||
"auxiliaryNames": [],
|
||||
"addresses": [
|
||||
{
|
||||
"careOf": null,
|
||||
"street": "Testitie 1",
|
||||
"postCode": "00100",
|
||||
"type": 1,
|
||||
"city": "HELSINKI",
|
||||
"country": "FI",
|
||||
"source": 0,
|
||||
"version": 1,
|
||||
"registrationDate": "2019-04-30",
|
||||
"endDate": null,
|
||||
"language": "fi"
|
||||
}
|
||||
],
|
||||
"companyForms": [
|
||||
{
|
||||
"version": 1,
|
||||
"registrationDate": "2001-04-03",
|
||||
"endDate": null,
|
||||
"name": "Osakeyhtiö",
|
||||
"type": "OY",
|
||||
"source": 0,
|
||||
"language": "fi"
|
||||
}
|
||||
],
|
||||
"liquidations": [],
|
||||
"businessLines": [
|
||||
{
|
||||
"order": 0,
|
||||
"version": 1,
|
||||
"registrationDate": "2017-12-31",
|
||||
"endDate": null,
|
||||
"name": "Computer programming activities",
|
||||
"source": 2,
|
||||
"language": "en"
|
||||
}
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"version": 1,
|
||||
"registrationDate": "2001-04-03",
|
||||
"endDate": null,
|
||||
"name": "Finnish",
|
||||
"source": 0,
|
||||
"language": "en"
|
||||
}
|
||||
],
|
||||
"registeredOffices": [
|
||||
{
|
||||
"order": 0,
|
||||
"version": 1,
|
||||
"registrationDate": "2001-04-03",
|
||||
"endDate": null,
|
||||
"name": "HELSINKI",
|
||||
"source": 0,
|
||||
"language": "fi"
|
||||
}
|
||||
],
|
||||
"contactDetails": [
|
||||
{
|
||||
"version": 1,
|
||||
"registrationDate": "2019-04-30",
|
||||
"endDate": null,
|
||||
"language": "fi",
|
||||
"value": "www.example.fi",
|
||||
"type": "Website",
|
||||
"source": 0
|
||||
}
|
||||
],
|
||||
"registeredEntries": [
|
||||
{
|
||||
"authority": 2,
|
||||
"register": 1,
|
||||
"status": 2,
|
||||
"registrationDate": "2001-06-01",
|
||||
"endDate": null,
|
||||
"description": "Registered",
|
||||
"language": "en"
|
||||
}
|
||||
],
|
||||
"businessIdChanges": []
|
||||
}
|
||||
]
|
||||
}
|
||||
101
tests/Fixtures/v3-search-response.json
Normal file
101
tests/Fixtures/v3-search-response.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"totalResults": 1,
|
||||
"companies": [
|
||||
{
|
||||
"businessId": {
|
||||
"value": "1639413-9",
|
||||
"registrationDate": "2001-04-03",
|
||||
"source": "YTJ"
|
||||
},
|
||||
"euId": {
|
||||
"value": "FIFOO123456",
|
||||
"source": "YTJ"
|
||||
},
|
||||
"names": [
|
||||
{
|
||||
"name": "Oy Example Ab",
|
||||
"type": "TOIM",
|
||||
"registrationDate": "2015-01-19",
|
||||
"endDate": null,
|
||||
"version": 1,
|
||||
"source": "YTJ"
|
||||
}
|
||||
],
|
||||
"mainBusinessLine": {
|
||||
"code": "62010",
|
||||
"descriptions": [
|
||||
{
|
||||
"languageCode": "3",
|
||||
"description": "Computer programming activities"
|
||||
}
|
||||
],
|
||||
"typeCodeSet": "TOL2008",
|
||||
"registrationDate": "2017-12-31",
|
||||
"source": "VERO"
|
||||
},
|
||||
"website": {
|
||||
"url": "www.example.fi",
|
||||
"registrationDate": "2019-04-30",
|
||||
"source": "YTJ"
|
||||
},
|
||||
"companyForms": [
|
||||
{
|
||||
"type": "OY",
|
||||
"descriptions": [
|
||||
{
|
||||
"languageCode": "1",
|
||||
"description": "Osakeyhtiö"
|
||||
},
|
||||
{
|
||||
"languageCode": "3",
|
||||
"description": "Limited company"
|
||||
}
|
||||
],
|
||||
"registrationDate": "2001-04-03",
|
||||
"endDate": null,
|
||||
"version": 1,
|
||||
"source": "YTJ"
|
||||
}
|
||||
],
|
||||
"companySituations": [],
|
||||
"registeredEntries": [
|
||||
{
|
||||
"type": "REK",
|
||||
"descriptions": [
|
||||
{
|
||||
"languageCode": "3",
|
||||
"description": "Registered"
|
||||
}
|
||||
],
|
||||
"registrationDate": "2001-06-01",
|
||||
"endDate": null,
|
||||
"register": "1",
|
||||
"authority": "2"
|
||||
}
|
||||
],
|
||||
"addresses": [
|
||||
{
|
||||
"type": 1,
|
||||
"street": "Testitie 1",
|
||||
"postCode": "00100",
|
||||
"postOffices": [
|
||||
{
|
||||
"city": "HELSINKI",
|
||||
"languageCode": "1",
|
||||
"municipalityCode": "091"
|
||||
}
|
||||
],
|
||||
"co": null,
|
||||
"country": "FI",
|
||||
"registrationDate": "2019-04-30",
|
||||
"source": "YTJ"
|
||||
}
|
||||
],
|
||||
"tradeRegisterStatus": "2",
|
||||
"status": "ACTIVE",
|
||||
"registrationDate": "2001-04-03",
|
||||
"endDate": null,
|
||||
"lastModified": "2023-06-15T10:30:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
tests/Pest.php
Normal file
9
tests/Pest.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
// uses(Tests\TestCase::class)->in('Feature');
|
||||
52
tests/Unit/BusinessDataFetcherTest.php
Normal file
52
tests/Unit/BusinessDataFetcherTest.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Ivuorinen\BusinessDataFetcher\BusinessDataFetcher;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyDetails;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Exceptions\ApiResponseErrorException;
|
||||
|
||||
function createFetcherWithMock(Response ...$responses): BusinessDataFetcher
|
||||
{
|
||||
$mock = new MockHandler($responses);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
return new BusinessDataFetcher($client);
|
||||
}
|
||||
|
||||
it('returns BisCompanyDetails array on success', function () {
|
||||
$json = file_get_contents(__DIR__ . '/../Fixtures/sample-response.json');
|
||||
$fetcher = createFetcherWithMock(new Response(200, [], $json));
|
||||
|
||||
$results = $fetcher->getBusinessInformation('1639413-9');
|
||||
|
||||
expect($results)->toBeArray()
|
||||
->and($results)->toHaveCount(1)
|
||||
->and($results[0])->toBeInstanceOf(BisCompanyDetails::class)
|
||||
->and($results[0]->businessId)->toBe('1639413-9');
|
||||
});
|
||||
|
||||
it('throws on non-200 status code', function () {
|
||||
$fetcher = createFetcherWithMock(
|
||||
new Response(500, [], '{"error": "Internal Server Error"}')
|
||||
);
|
||||
|
||||
$fetcher->getBusinessInformation('1639413-9');
|
||||
})->throws(ApiResponseErrorException::class);
|
||||
|
||||
it('throws on malformed JSON', function () {
|
||||
$fetcher = createFetcherWithMock(
|
||||
new Response(200, [], 'not-json')
|
||||
);
|
||||
|
||||
$fetcher->getBusinessInformation('1639413-9');
|
||||
})->throws(JsonException::class);
|
||||
|
||||
it('throws when results key is missing', function () {
|
||||
$fetcher = createFetcherWithMock(
|
||||
new Response(200, [], '{"type": "test"}')
|
||||
);
|
||||
|
||||
$fetcher->getBusinessInformation('1639413-9');
|
||||
})->throws(ApiResponseErrorException::class);
|
||||
43
tests/Unit/v1/Dto/BisCompanyDetailsTest.php
Normal file
43
tests/Unit/v1/Dto/BisCompanyDetailsTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyDetails;
|
||||
|
||||
it('hydrates from sample JSON fixture', function () {
|
||||
$json = json_decode(
|
||||
file_get_contents(__DIR__ . '/../../../Fixtures/sample-response.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
|
||||
$mapper = (new MapperBuilder())
|
||||
->allowSuperfluousKeys()
|
||||
->mapper();
|
||||
|
||||
$result = $mapper->map(BisCompanyDetails::class, $json['results'][0]);
|
||||
|
||||
expect($result)
|
||||
->toBeInstanceOf(BisCompanyDetails::class)
|
||||
->and($result->businessId)->toBe('1639413-9')
|
||||
->and($result->registrationDate)->toBe('2001-04-03')
|
||||
->and($result->companyForm)->toBe('OY')
|
||||
->and($result->name)->toBe('Oy Example Ab')
|
||||
->and($result->names)->toHaveCount(1)
|
||||
->and($result->names[0]->name)->toBe('Oy Example Ab')
|
||||
->and($result->names[0]->getLanguageString())->toBe('finnish')
|
||||
->and($result->names[0]->getSourceText())->toBe('common')
|
||||
->and($result->addresses)->toHaveCount(1)
|
||||
->and($result->addresses[0]->street)->toBe('Testitie 1')
|
||||
->and($result->addresses[0]->postCode)->toBe('00100')
|
||||
->and($result->addresses[0]->city)->toBe('HELSINKI')
|
||||
->and($result->companyForms)->toHaveCount(1)
|
||||
->and($result->companyForms[0]->name)->toBe('Osakeyhtiö')
|
||||
->and($result->businessLines)->toHaveCount(1)
|
||||
->and($result->businessLines[0]->name)->toBe('Computer programming activities')
|
||||
->and($result->registeredEntries)->toHaveCount(1)
|
||||
->and($result->registeredEntries[0]->getAuthorityString())->toBe('Finnish Patent and Registration Office')
|
||||
->and($result->registeredEntries[0]->getRegisterString())->toBe('Trade Register')
|
||||
->and($result->contactDetails)->toHaveCount(1)
|
||||
->and($result->contactDetails[0]->value)->toBe('www.example.fi');
|
||||
});
|
||||
87
tests/Unit/v1/Dto/DtoHydrationTest.php
Normal file
87
tests/Unit/v1/Dto/DtoHydrationTest.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisAddress;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyBusinessIdChange;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyBusinessLine;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyContactDetail;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyForm;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyLanguage;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyLiquidation;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyName;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyRegisteredEntry;
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Dto\BisCompanyRegisteredOffice;
|
||||
|
||||
function valinorMapper(): CuyZ\Valinor\Mapper\TreeMapper
|
||||
{
|
||||
return (new MapperBuilder())
|
||||
->allowSuperfluousKeys()
|
||||
->mapper();
|
||||
}
|
||||
|
||||
it('hydrates all DTOs from arrays', function (string $class, array $data, string $property, mixed $expected) {
|
||||
$dto = valinorMapper()->map($class, $data);
|
||||
expect($dto)->toBeInstanceOf($class)
|
||||
->and($dto->$property)->toBe($expected);
|
||||
})->with([
|
||||
'BisAddress' => [
|
||||
BisAddress::class,
|
||||
['type' => 1, 'street' => 'Katu 1', 'postCode' => '00100', 'city' => 'HELSINKI', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'street',
|
||||
'Katu 1',
|
||||
],
|
||||
'BisCompanyName' => [
|
||||
BisCompanyName::class,
|
||||
['order' => 0, 'name' => 'Test Oy', 'source' => 1, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'name',
|
||||
'Test Oy',
|
||||
],
|
||||
'BisCompanyForm' => [
|
||||
BisCompanyForm::class,
|
||||
['name' => 'Osakeyhtiö', 'type' => 'OY', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'type',
|
||||
'OY',
|
||||
],
|
||||
'BisCompanyBusinessLine' => [
|
||||
BisCompanyBusinessLine::class,
|
||||
['order' => 0, 'name' => 'IT', 'source' => 2, 'version' => 1, 'language' => 'en', 'registrationDate' => '2020-01-01'],
|
||||
'name',
|
||||
'IT',
|
||||
],
|
||||
'BisCompanyLanguage' => [
|
||||
BisCompanyLanguage::class,
|
||||
['name' => 'Finnish', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'language',
|
||||
'fi',
|
||||
],
|
||||
'BisCompanyRegisteredEntry' => [
|
||||
BisCompanyRegisteredEntry::class,
|
||||
['authority' => 2, 'register' => 1, 'status' => 2, 'description' => 'Registered', 'language' => 'en', 'registrationDate' => '2020-01-01'],
|
||||
'description',
|
||||
'Registered',
|
||||
],
|
||||
'BisCompanyLiquidation' => [
|
||||
BisCompanyLiquidation::class,
|
||||
['name' => 'Bankruptcy', 'type' => 'K', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'name',
|
||||
'Bankruptcy',
|
||||
],
|
||||
'BisCompanyRegisteredOffice' => [
|
||||
BisCompanyRegisteredOffice::class,
|
||||
['order' => 0, 'name' => 'Helsinki', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'name',
|
||||
'Helsinki',
|
||||
],
|
||||
'BisCompanyContactDetail' => [
|
||||
BisCompanyContactDetail::class,
|
||||
['value' => 'www.test.fi', 'type' => 'Website', 'source' => 0, 'version' => 1, 'language' => 'fi', 'registrationDate' => '2020-01-01'],
|
||||
'value',
|
||||
'www.test.fi',
|
||||
],
|
||||
'BisCompanyBusinessIdChange' => [
|
||||
BisCompanyBusinessIdChange::class,
|
||||
['description' => 'Changed', 'reason' => '1', 'oldBusinessId' => '1234567-8', 'newBusinessId' => '8765432-1', 'source' => 0, 'language' => 'fi', 'change' => 5],
|
||||
'oldBusinessId',
|
||||
'1234567-8',
|
||||
],
|
||||
]);
|
||||
30
tests/Unit/v1/Traits/HasAuthorityTest.php
Normal file
30
tests/Unit/v1/Traits/HasAuthorityTest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits\HasAuthority;
|
||||
|
||||
function makeAuthorityObject(int $authority): object
|
||||
{
|
||||
return new class ($authority) {
|
||||
use HasAuthority;
|
||||
|
||||
public function __construct(public int $authority)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('returns Tax Administration for authority 1', function () {
|
||||
expect(makeAuthorityObject(1)->getAuthorityString())->toBe('Tax Administration');
|
||||
});
|
||||
|
||||
it('returns Finnish Patent and Registration Office for authority 2', function () {
|
||||
expect(makeAuthorityObject(2)->getAuthorityString())->toBe('Finnish Patent and Registration Office');
|
||||
});
|
||||
|
||||
it('returns Population Register for authority 3', function () {
|
||||
expect(makeAuthorityObject(3)->getAuthorityString())->toBe('Population Register');
|
||||
});
|
||||
|
||||
it('returns unknown for unknown authority', function () {
|
||||
expect(makeAuthorityObject(99)->getAuthorityString())->toBe('unknown:99');
|
||||
});
|
||||
41
tests/Unit/v1/Traits/HasChangeTest.php
Normal file
41
tests/Unit/v1/Traits/HasChangeTest.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits\HasChange;
|
||||
|
||||
function makeChangeObject(string|int|null $change): object
|
||||
{
|
||||
return new class ($change) {
|
||||
use HasChange;
|
||||
|
||||
public function __construct(public string|int|null $change)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('maps all integer change codes correctly', function (int $code, string $expected) {
|
||||
expect(makeChangeObject($code)->getChangeString())->toBe($expected);
|
||||
})->with([
|
||||
[2, 'Business ID removal'],
|
||||
[3, 'Combining of double IDs'],
|
||||
[5, 'ID changed'],
|
||||
[44, 'Fusion'],
|
||||
[45, 'Operator continuing VAT activities'],
|
||||
[46, 'Relation to predecessor'],
|
||||
[47, 'Division'],
|
||||
[48, 'Bankruptcy relationship'],
|
||||
[49, 'Operations continued by a private trader'],
|
||||
[57, 'Partial division'],
|
||||
]);
|
||||
|
||||
it('maps FUU string to Fusion', function () {
|
||||
expect(makeChangeObject('FUU')->getChangeString())->toBe('Fusion');
|
||||
});
|
||||
|
||||
it('maps DIF string to Division', function () {
|
||||
expect(makeChangeObject('DIF')->getChangeString())->toBe('Division');
|
||||
});
|
||||
|
||||
it('returns unknown for unrecognized change', function () {
|
||||
expect(makeChangeObject(999)->getChangeString())->toBe('unknown:999');
|
||||
});
|
||||
30
tests/Unit/v1/Traits/HasLanguageTest.php
Normal file
30
tests/Unit/v1/Traits/HasLanguageTest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits\HasLanguage;
|
||||
|
||||
function makeLanguageObject(?string $language): object
|
||||
{
|
||||
return new class ($language) {
|
||||
use HasLanguage;
|
||||
|
||||
public function __construct(public ?string $language)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('returns finnish for fi', function () {
|
||||
expect(makeLanguageObject('fi')->getLanguageString())->toBe('finnish');
|
||||
});
|
||||
|
||||
it('returns english for en', function () {
|
||||
expect(makeLanguageObject('en')->getLanguageString())->toBe('english');
|
||||
});
|
||||
|
||||
it('returns swedish for sv', function () {
|
||||
expect(makeLanguageObject('sv')->getLanguageString())->toBe('swedish');
|
||||
});
|
||||
|
||||
it('returns unknown for unrecognized language', function () {
|
||||
expect(makeLanguageObject('de')->getLanguageString())->toBe('unknown:de');
|
||||
});
|
||||
35
tests/Unit/v1/Traits/HasRegisterTest.php
Normal file
35
tests/Unit/v1/Traits/HasRegisterTest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits\HasRegister;
|
||||
|
||||
function makeRegisterObject(?int $register): object
|
||||
{
|
||||
return new class ($register) {
|
||||
use HasRegister;
|
||||
|
||||
public function __construct(public ?int $register)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('maps all register codes correctly', function (int $code, string $expected) {
|
||||
expect(makeRegisterObject($code)->getRegisterString())->toBe($expected);
|
||||
})->with([
|
||||
[1, 'Trade Register'],
|
||||
[2, 'Register of Foundations'],
|
||||
[3, 'Register of Associations'],
|
||||
[4, 'Tax Administration'],
|
||||
[5, 'Prepayment Register'],
|
||||
[6, 'VAT Register'],
|
||||
[7, 'Employer Register'],
|
||||
[8, 'Register of bodies liable for tax on insurance premiums'],
|
||||
]);
|
||||
|
||||
it('returns unknown for unrecognized register', function () {
|
||||
expect(makeRegisterObject(99)->getRegisterString())->toBe('unknown:99');
|
||||
});
|
||||
|
||||
it('returns unknown for null register', function () {
|
||||
expect(makeRegisterObject(null)->getRegisterString())->toBe('unknown:');
|
||||
});
|
||||
34
tests/Unit/v1/Traits/HasSourceTest.php
Normal file
34
tests/Unit/v1/Traits/HasSourceTest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Ivuorinen\BusinessDataFetcher\v1\Traits\HasSource;
|
||||
|
||||
function makeSourceObject(?int $source): object
|
||||
{
|
||||
return new class ($source) {
|
||||
use HasSource;
|
||||
|
||||
public function __construct(public ?int $source)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('returns common for source 0', function () {
|
||||
expect(makeSourceObject(0)->getSourceText())->toBe('common');
|
||||
});
|
||||
|
||||
it('returns Finnish Patent and Registration Office for source 1', function () {
|
||||
expect(makeSourceObject(1)->getSourceText())->toBe('Finnish Patent and Registration Office');
|
||||
});
|
||||
|
||||
it('returns Tax Administration for source 2', function () {
|
||||
expect(makeSourceObject(2)->getSourceText())->toBe('Tax Administration');
|
||||
});
|
||||
|
||||
it('returns Business Information System for source 3', function () {
|
||||
expect(makeSourceObject(3)->getSourceText())->toBe('Business Information System');
|
||||
});
|
||||
|
||||
it('returns empty string for unknown source', function () {
|
||||
expect(makeSourceObject(99)->getSourceText())->toBe('');
|
||||
});
|
||||
73
tests/Unit/v3/ClientTest.php
Normal file
73
tests/Unit/v3/ClientTest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Client as V3Client;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Dto\CompanySearchResult;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Dto\PostCodeEntry;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Exceptions\V3ApiException;
|
||||
|
||||
function createV3ClientWithMock(Response ...$responses): V3Client
|
||||
{
|
||||
$mock = new MockHandler($responses);
|
||||
$client = new Client(['handler' => HandlerStack::create($mock)]);
|
||||
return new V3Client($client);
|
||||
}
|
||||
|
||||
it('searches companies successfully', function () {
|
||||
$json = file_get_contents(__DIR__ . '/../../Fixtures/v3-search-response.json');
|
||||
$client = createV3ClientWithMock(new Response(200, [], $json));
|
||||
|
||||
$result = $client->searchCompanies(name: 'Example');
|
||||
|
||||
expect($result)
|
||||
->toBeInstanceOf(CompanySearchResult::class)
|
||||
->and($result->totalResults)->toBe(1)
|
||||
->and($result->companies)->toHaveCount(1)
|
||||
->and($result->companies[0]->businessId->value)->toBe('1639413-9')
|
||||
->and($result->companies[0]->names[0]->name)->toBe('Oy Example Ab')
|
||||
->and($result->companies[0]->tradeRegisterStatus)->toBe('2')
|
||||
->and($result->companies[0]->addresses[0]->street)->toBe('Testitie 1');
|
||||
});
|
||||
|
||||
it('throws V3ApiException on request failure for search', function () {
|
||||
$client = createV3ClientWithMock(new Response(500, [], '{"message":"error"}'));
|
||||
$client->searchCompanies(name: 'Test');
|
||||
})->throws(V3ApiException::class);
|
||||
|
||||
it('retrieves description as plain text', function () {
|
||||
$client = createV3ClientWithMock(new Response(200, [], 'Trade Register'));
|
||||
$result = $client->getDescription('REK', 'en');
|
||||
|
||||
expect($result)->toBe('Trade Register');
|
||||
});
|
||||
|
||||
it('throws V3ApiException on description failure', function () {
|
||||
$client = createV3ClientWithMock(new Response(400, [], '{"message":"bad request"}'));
|
||||
$client->getDescription('INVALID');
|
||||
})->throws(V3ApiException::class);
|
||||
|
||||
it('retrieves post codes', function () {
|
||||
$json = (string) json_encode([
|
||||
['postCode' => '00100', 'city' => 'HELSINKI', 'active' => true, 'languageCode' => '1', 'municipalityCode' => '091'],
|
||||
['postCode' => '00200', 'city' => 'HELSINKI', 'active' => true, 'languageCode' => '1', 'municipalityCode' => '091'],
|
||||
]);
|
||||
$client = createV3ClientWithMock(new Response(200, [], $json));
|
||||
|
||||
$results = $client->getPostCodes('fi');
|
||||
|
||||
expect($results)->toHaveCount(2)
|
||||
->and($results[0])->toBeInstanceOf(PostCodeEntry::class)
|
||||
->and($results[0]->postCode)->toBe('00100')
|
||||
->and($results[0]->city)->toBe('HELSINKI')
|
||||
->and($results[1]->postCode)->toBe('00200');
|
||||
});
|
||||
|
||||
it('returns stream for all companies download', function () {
|
||||
$client = createV3ClientWithMock(new Response(200, [], 'zip-content'));
|
||||
$stream = $client->getAllCompanies();
|
||||
|
||||
expect($stream->getContents())->toBe('zip-content');
|
||||
});
|
||||
53
tests/Unit/v3/Dto/CompanyTest.php
Normal file
53
tests/Unit/v3/Dto/CompanyTest.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
use CuyZ\Valinor\MapperBuilder;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Dto\Company;
|
||||
use Ivuorinen\BusinessDataFetcher\v3\Dto\CompanySearchResult;
|
||||
|
||||
it('hydrates Company from v3 search response fixture', function () {
|
||||
$json = json_decode(
|
||||
file_get_contents(__DIR__ . '/../../../Fixtures/v3-search-response.json'),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
|
||||
$mapper = (new MapperBuilder())
|
||||
->allowSuperfluousKeys()
|
||||
->mapper();
|
||||
|
||||
$result = $mapper->map(CompanySearchResult::class, $json);
|
||||
|
||||
expect($result->totalResults)->toBe(1)
|
||||
->and($result->companies)->toHaveCount(1);
|
||||
|
||||
$company = $result->companies[0];
|
||||
|
||||
expect($company)->toBeInstanceOf(Company::class)
|
||||
->and($company->businessId->value)->toBe('1639413-9')
|
||||
->and($company->businessId->registrationDate)->toBe('2001-04-03')
|
||||
->and($company->euId)->not->toBeNull()
|
||||
->and($company->euId->value)->toBe('FIFOO123456')
|
||||
->and($company->names)->toHaveCount(1)
|
||||
->and($company->names[0]->name)->toBe('Oy Example Ab')
|
||||
->and($company->names[0]->type)->toBe('TOIM')
|
||||
->and($company->mainBusinessLine)->not->toBeNull()
|
||||
->and($company->mainBusinessLine->code)->toBe('62010')
|
||||
->and($company->mainBusinessLine->descriptions)->toHaveCount(1)
|
||||
->and($company->mainBusinessLine->descriptions[0]->description)->toBe('Computer programming activities')
|
||||
->and($company->website)->not->toBeNull()
|
||||
->and($company->website->url)->toBe('www.example.fi')
|
||||
->and($company->companyForms)->toHaveCount(1)
|
||||
->and($company->companyForms[0]->type)->toBe('OY')
|
||||
->and($company->companyForms[0]->descriptions)->toHaveCount(2)
|
||||
->and($company->registeredEntries)->toHaveCount(1)
|
||||
->and($company->registeredEntries[0]->register)->toBe('1')
|
||||
->and($company->registeredEntries[0]->authority)->toBe('2')
|
||||
->and($company->addresses)->toHaveCount(1)
|
||||
->and($company->addresses[0]->street)->toBe('Testitie 1')
|
||||
->and($company->addresses[0]->postOffices)->toHaveCount(1)
|
||||
->and($company->addresses[0]->postOffices[0]->city)->toBe('HELSINKI')
|
||||
->and($company->tradeRegisterStatus)->toBe('2')
|
||||
->and($company->status)->toBe('ACTIVE')
|
||||
->and($company->lastModified)->toBe('2023-06-15T10:30:00Z');
|
||||
});
|
||||
Reference in New Issue
Block a user