diff --git a/.squad/agents/ash/history.md b/.squad/agents/ash/history.md index 941e856fb..eeae2c581 100644 --- a/.squad/agents/ash/history.md +++ b/.squad/agents/ash/history.md @@ -16,6 +16,7 @@ - `src\Refitter.SourceGenerator\RefitterSourceGenerator.cs` still has a same-directory hint-name collision risk because `CreateUniqueHintName(...)` hashes only the parent directory, not the full `.refitter` path. - In the source generator, treat `AdditionalText.GetText(...)` as nullable and convert read/encoding failures into diagnostics; null-forgiving it weakens failure reporting. - Review gate for the next safety pass: require targeted tests for source-generator hint-name collisions, OpenAPI-title sanitization including `<`/`>`, and MSBuild runtime-discovery timeout behavior before accepting cleanup claims. +- 2026-04-28: Approved Parker/Lambert e-conomic multi-spec merge fix after verifying canonical JSON-token equivalence accepts duplicate recursive/shared schemas, conflicting duplicate path/schema/definition/security entries still fail fast, and focused net10.0 TUnit coverage passes. ## Core Context diff --git a/.squad/agents/dallas/history.md b/.squad/agents/dallas/history.md index d7beb46e5..53452960d 100644 --- a/.squad/agents/dallas/history.md +++ b/.squad/agents/dallas/history.md @@ -8,6 +8,8 @@ ## Learnings +- **2026-04-28T15:21:48.369+02:00 e-conomic multi-spec tooling verdict:** `test\economic.refitter` parses correctly and resolves both relative `openApiPaths`; CLI/MSBuild/source-generator all reach core multi-document merge, where duplicate equivalent schemas (`Error`, then `ProblemDetails`) trigger the fail-fast merge path before validation, so `--skip-validation` cannot help. + - Team initialized on 2026-04-16. - **2026-04-28 issue #1045 tooling verdict:** current HEAD CLI accepts `.refitter` files that use only `openApiPaths`; the quoted `'openApiPath' is required` failure string is stale pre-#1057 behavior, while the remaining normalization gap is a core/library consistency concern rather than a current CLI/MSBuild break. - **Issue #998 findings (2026-04-16):** MSBuild's first-clean-build path was the real tooling bug; CLI settings loading and the default single-file `Output.cs` behavior were otherwise correct. @@ -66,3 +68,11 @@ - Ripley's triage keeps docs/help drift first, then settings/spec-path normalization, then shared `GeneratedFile:` marker cleanup, which matches Dallas's current tooling/doc seams. - Lambert's baseline scan stays green for restore/build/test/format, but live-URL tests remain environment-sensitive and should not be used as tooling stability evidence during cleanup. + +## 2026-04-28: e-conomic Multi-Spec Tooling Validation (PRIMARY FIX IN CORE) +- `test\economic.refitter` parses correctly, resolves both relative `openApiPaths` under `test\OpenAPI\v3.0\`, each spec generates independently. +- CLI/MSBuild/source-generator all reach core merge path where duplicate equivalent schemas (`Error`, `ProblemDetails`) trigger fail-fast before validation. +- Root cause confirmed in `Refitter.Core` not tooling; `--skip-validation` cannot help because merge happens before validation. +- Proposal: primary fix belongs in core merge semantics (allow duplicate paths/schemas when semantically equivalent); do not split `.refitter` into multiple generation runs. +- Tooling follow-up (post-core-fix): improve CLI error guidance for merge failures, add `openApiPaths` regression coverage, add relative-path resolution tests, add merge-failure diagnostic tests. + diff --git a/.squad/agents/lambert/history.md b/.squad/agents/lambert/history.md index 04e123c88..a98294f9b 100644 --- a/.squad/agents/lambert/history.md +++ b/.squad/agents/lambert/history.md @@ -8,6 +8,10 @@ ## Learnings +- **2026-04-28T15:21:48.369+02:00 e-conomic OpenApiPaths regression coverage:** Added intended compile-backed regression coverage for the real `economic-products.json` + `economic-webhooks.json` inputs through `RefitGeneratorSettings.OpenApiPaths`. Current HEAD fails before code generation with duplicate schema `Error`, so the non-empty/buildable assertions become active evidence once the merge fix lands. + +- **2026-04-28T15:21:48.369+02:00 e-conomic multi-spec repro:** `test\economic.refitter` fails during generation, not compilation, because `economic-products.json` and `economic-webhooks.json` both define identical `Error` and `ProblemDetails` component schemas; current merge logic throws on the first duplicate schema key (`Error`). Regression coverage should distinguish identical shared schemas from conflicting schema collisions. + - Team initialized on 2026-04-16. - **2026-04-28T12:02:17.298+02:00 issue #1045 evidence split:** Current HEAD already covers `.refitter` `openApiPaths` validation/execution and direct `RefitGenerator.CreateAsync(settings)` multi-spec usage. The reliable tester posture is to separate the CLI/settings-file claim from the direct-core-consumer claim, because they need different proofs and the current code/test matrix supports neither regression at HEAD. - **2026-04-25 CLI help repro:** src\Refitter\Program.cs intentionally rewrites a no-argument invocation to --help, exits 0, and emits Spectre.Console.Cli help output. Tests in src\Refitter.Tests\GenerateCommandTests.cs should assert semantic help markers like usage, sections, and option names rather than exact formatter-driven spacing. @@ -71,3 +75,15 @@ - **2026-04-26 final revalidation after cleanup landing:** Current HEAD is green again on the trusted loop: `dotnet restore src\Refitter.slnx`, `dotnet build -c Release src\Refitter.slnx --no-restore`, `dotnet test -c Release src\Refitter.slnx --no-build`, and `dotnet format --verify-no-changes src\Refitter.slnx --no-restore`. The solution test pass now reports 1918 succeeded / 0 failed, and the trusted coverage lane (`dotnet test --project src\Refitter.Tests\Refitter.Tests.csproj -c Release --coverage --coverage-output coverage.cobertura.xml --coverage-output-format xml`) passed 1862 / 1862 with `Refitter.Core.dll` 95.05% line / 96.47% block, `refitter.dll` 97.10% / 99.11%, `Refitter.MSBuild.dll` back to 100% / 100%, and generated `RuntimeProof.dll` unchanged at 80.93% / 71.58%. +## 2026-04-28: e-conomic Multi-Spec Merge Failure Repro (APPROVED FOR FIX) +- CLI failure repro: `dotnet .\src\Refitter\bin\Release\net9.0\refitter.dll --settings-file test\economic.refitter --no-banner --no-logging --simple-output` +- Failure: `InvalidOperationException: Cannot merge OpenAPI documents because a duplicate schema 'Error' was found.` +- Root cause: `economic-products.json` and `economic-webhooks.json` both define identical `Error` and `ProblemDetails` component schemas; current merge throws on first duplicate. +- Approved test coverage plan: + 1. `OpenApiDocumentFactoryMergeTests`: add `Merge_With_Equivalent_Duplicate_Schema_Does_Not_Throw` test (OpenAPI 3 docs with identical schema key/body); preserve collision conflict tests + 2. Optional e-conomic-shaped regression for `Error` schema with descriptions, nullable properties, extensions, `additionalProperties: false` + 3. Compile-backed regression: two specs with identical error schema and distinct paths; assert generation succeeds and code builds + 4. Post-fix generated-code validation for `test\economic.refitter`; triage any post-merge generation failures separately +- Regression gates: existing merge-collision tests must continue passing + +- **2026-04-28T15:21:48.369+02:00 e-conomic multi-spec merge final validation:** Release build, focused `OpenApiDocumentFactoryMergeTests` (15 tests), focused `Issue1016_MultiSpecSchemaMergeTests` (7 tests), full Release solution tests, format verification, and the real `test\economic.refitter` CLI generation all passed. The required CLI command generated `test\GeneratedCode\economic.cs` successfully from `economic-products.json` + `economic-webhooks.json` without duplicate schema merge failures. diff --git a/.squad/agents/parker/history.md b/.squad/agents/parker/history.md index 786d925a1..a34654ba1 100644 --- a/.squad/agents/parker/history.md +++ b/.squad/agents/parker/history.md @@ -16,6 +16,7 @@ - 2026-04-26: Do not refactor ParameterExtractor or interface-emission flow until compile-backed public regressions cover multipart generation and dynamic-query behavior; ParameterExtractorPrivateCoverageTests currently leans on reflection plus RuntimeHelpers.GetUninitializedObject and is too implementation-coupled to be the only safety net. - 2026-04-26: The generator pipeline is duplicated between src\Refitter.Core\RefitGenerator.cs Generate()/GenerateMultipleFiles() and among src\Refitter.Core\RefitInterfaceGenerator.cs, RefitMultipleInterfaceGenerator.cs, and RefitMultipleInterfaceByTagGenerator.cs; treat those as Ash-review cleanups because ordering and emitted signature shape are behavior-sensitive. - 2026-04-28T12:02:17.298+02:00: Issue #1045 is outdated at current HEAD for Refitter.Core. RefitGenerator.CreateAsync() leaves RefitGeneratorSettings.OpenApiPath null when only OpenApiPaths is supplied, but the core path loader branches on OpenApiPaths first and no downstream Refitter.Core consumer dereferences OpenApiPath afterward; mutating OpenApiPath inside core would be an observable settings-semantics change that conflicts with existing regression expectations. +- 2026-04-28T15:21:48.369+02:00: e-conomic multi-spec generation fails before code emission in OpenApiDocumentFactory.Merge() on duplicate schema Error; the vendor specs define identical Error schemas, but AreEquivalent() uses System.Text.Json Serializer.Serialize() on NJsonSchema objects, which can hit object cycles and incorrectly treats equivalent shared schemas as conflicts. ## Core Context @@ -52,3 +53,12 @@ - Ripley's AI-slop sequencing kept Parker's low-risk-first generator guidance intact: docs/help drift first, then settings/marker cleanup, and only later Ash-reviewed generator dedup. - Lambert's baseline scan confirmed the repo is currently green on restore/build/test/format, which keeps compile-backed regression work as the gate before any deeper generator cleanup. + +## 2026-04-28: e-conomic Multi-Spec Merge Failure Analysis (APPROVED FOR FIX) +- e-conomic failure is `OpenApiDocumentFactory.Merge()` throwing on duplicate equivalent schemas (`Error`, `ProblemDetails`). +- Root cause: `AreEquivalent()` uses `System.Text.Json Serializer.Serialize()` on NJsonSchema objects; object cycles cause false-negative on identical schemas. +- Proposed fix: replace `AreEquivalent()` with OpenAPI-aware canonical comparison using NSwag `ToJson()` or equivalent; keep fail-fast for genuine conflicts. +- Implementation file: `src\Refitter.Core\OpenApiDocumentFactory.cs` → `MergeIfMissingOrThrowOnConflict()` / `AreEquivalent()` +- Test coverage: merge equivalence tests, e-conomic-shaped regressions, compile-backed generation validation +- Constraints: preserve clone-first/non-mutating merge, don't rename schemas, keep relative-path behavior across tooling surfaces +- 2026-04-28T15:21:48.369+02:00: Implemented duplicate schema equivalence by comparing normalized NSwag/NJsonSchema JSON tokens, with a schema-specific canonical fallback for referenced/cyclic graphs that cannot serialize standalone. This keeps merge semantics fail-fast for real path/schema/definition/security conflicts while allowing equivalent e-conomic `ProblemDetails`/`Error` duplicates. diff --git a/.squad/agents/ripley/history.md b/.squad/agents/ripley/history.md index f9f4ea6a1..f89a67bfe 100644 --- a/.squad/agents/ripley/history.md +++ b/.squad/agents/ripley/history.md @@ -10,6 +10,7 @@ - Team initialized on 2026-04-16. - 2026-04-28T12:02:17.298+02:00: Issue #1045's new multi-path `.refitter` comment does not reproduce at current HEAD; current CLI validation accepts `openApiPaths`, normalizes relative entries, and the live repro instead surfaced expected merge-collision behavior only when duplicate path specs were used. +- 2026-04-28T15:21:48.369+02:00: The e-conomic multi-spec failure is a core `OpenApiDocumentFactory` merge-equivalence problem: `economic-products.json` and `economic-webhooks.json` share equivalent `Error` and `ProblemDetails` component schemas, but current NSwag-object serialization comparison false-negatives and throws before validation/generation. ## Core Context @@ -32,3 +33,10 @@ - The best remaining behavior-safe seam is still split settings/spec-path normalization across `src\Refitter\SettingsValidator.cs`, `src\Refitter\GenerateCommand.cs`, and `src\Refitter.SourceGenerator\RefitterSourceGenerator.cs`; those paths still disagree on URL detection and only some flows normalize `OpenApiPaths`. - The next best tooling seam is the duplicated `GeneratedFile:` marker contract between `src\Refitter\GenerateCommand.cs` and `src\Refitter.MSBuild\RefitterGenerateTask.cs`. - After those two, the backlog drops into lower-priority cleanup/refactor territory such as source-generator info-diagnostic shaping, duplicate test pruning, dead output-model cleanup, and deeper core generator dedup. + +## 2026-04-28: e-conomic Multi-Spec Merge Failure (APPROVED FOR FIX) +- Squad triaged the `test\economic.refitter` failure to `OpenApiDocumentFactory.Merge()` throwing on duplicate equivalent schemas. +- `economic-products.json` and `economic-webhooks.json` share identical `Error` and `ProblemDetails` component schemas; current `AreEquivalent()` calls `Serializer.Serialize()` on NJsonSchema objects and false-negatives on object cycles. +- Fix approved: replace `AreEquivalent()` with OpenAPI-aware semantic comparison using NSwag canonical JSON representation; keep fail-fast for genuine conflicts. +- Gates: merge equivalence tests, e-conomic-shaped regressions, compile-backed validation; existing collision tests must pass. +- Constraints: preserve relative-path behavior, don't edit specs, don't rename schemas in merge. diff --git a/.squad/decisions.md b/.squad/decisions.md index 3239a61a3..d50d3c0e2 100644 --- a/.squad/decisions.md +++ b/.squad/decisions.md @@ -272,3 +272,81 @@ - 2026-04-26: Have all agents use GPT-5.5 for the rest of this session only. - 2026-04-26: Commit changes as frequent as possible in small logical groups for a detailed progress history. + +## 2026-04-28 + +### e-conomic Multi-Spec OpenAPI Merge Failure + +**Leads:** Ripley (analysis), Parker (core finding), Dallas (tooling validation) +**Tester:** Lambert +**Status:** TRIAGED AND APPROVED FOR FIX + +#### Findings + +- `test\economic.refitter` triggers merge failure in `OpenApiDocumentFactory.Merge()` while combining `economic-products.json` and `economic-webhooks.json`. +- Exception: `InvalidOperationException: Cannot merge OpenAPI documents because a duplicate schema 'Error' was found.` +- Root cause: `AreEquivalent()` calls `Serializer.Serialize()` on NSwag/NJsonSchema objects; object-cycle behavior returns `false` for semantically identical schemas. +- Both `Error` and `ProblemDetails` are duplicated and textually equivalent in source JSON; merge fails at `Error` first. +- Issue is **not** in `.refitter` parsing, path resolution, or individual spec generation—each spec generates successfully in isolation. +- CLI, source generator, and MSBuild all reach the same merge failure point before code output. + +#### Fix Ownership and Scope + +**Primary owner:** `Refitter.Core` → `src\Refitter.Core\OpenApiDocumentFactory.cs` → `MergeIfMissingOrThrowOnConflict()` / `AreEquivalent()` + +- Replace `AreEquivalent()` with OpenAPI-aware semantic equivalence check (use NSwag `ToJson()` or canonical JSON representation). +- Keep fail-fast policy for **genuinely conflicting** duplicate path/schema/definition/security entries. +- Keep clone-first/non-mutating merge behavior. +- Do **not** edit e-conomic OpenAPI specs; do **not** rename schemas in merge. + +#### Approved Test Coverage and Gates + +1. Add `OpenApiDocumentFactoryMergeTests`: + - `Merge_With_Equivalent_Duplicate_Schema_Does_Not_Throw` — OpenAPI 3 docs with identical schema key and body; both paths preserved. + - Preserve existing `Merge_With_Schema_Collision_Throws_And_Does_Not_Mutate_Inputs` for conflicts. + - Optional e-conomic-shaped regression for `Error` schema with descriptions, nullable properties, extensions, `additionalProperties: false`. + +2. Compile-backed regression: two specs with identical common error schema and distinct paths; assert generation succeeds and generated code builds. + +3. Run generated-code validation for `test\economic.refitter` after merge fix; triage any post-merge generation failure separately. + +4. Existing merge-collision tests must continue passing. + +#### Constraints + +- Preserve relative-path behavior across CLI and source generator. +- Keep `openApiPaths` semantics consistent. +- OpenAPI 3 `components.schemas` and Swagger 2 `definitions` equivalence must remain aligned. +- Do not hide genuine schema differences in comparison. + +### Ash economic merge review + +- Date: 2026-04-28T15:21:48.369+02:00 + +Verdict: APPROVED + +Rationale: +- `OpenApiDocumentFactory.Merge()` still fails fast on genuinely conflicting duplicate paths, schemas, Swagger 2 definitions, and security schemes while accepting equivalent duplicate schemas. +- Equivalence is based on normalized OpenAPI/NSwag/NJsonSchema JSON tokens rather than object identity; the recursive schema test covers cyclic schema graphs and the e-conomic regression verifies real multi-spec generation remains buildable. +- Focused validation passed for `OpenApiDocumentFactoryMergeTests`, `Issue1016_MultiSpecSchemaMergeTests`, and the duplicate-path merge coverage via the net10.0 TUnit executable. A first `dotnet test --filter` attempt failed because this TUnit project does not support that option, not because of product/test failure. + +### Lambert e-conomic validation + +- Date: 2026-04-28T15:21:48.369+02:00 +- Validation result: PASS +- Evidence: + - `dotnet build -c Release src\Refitter.slnx` passed. + - Focused `OpenApiDocumentFactoryMergeTests` passed: 15 succeeded / 0 failed. + - Focused `Issue1016_MultiSpecSchemaMergeTests` passed: 7 succeeded / 0 failed. + - `dotnet test -c Release src\Refitter.slnx` passed. + - `dotnet format --verify-no-changes src\Refitter.slnx` passed. + - `dotnet .\src\Refitter\bin\Release\net9.0\refitter.dll --settings-file test\economic.refitter --no-banner --no-logging --simple-output` passed and generated `test\GeneratedCode\economic.cs`. +- Decision signal: the e-conomic duplicate equivalent schema merge path is validated by focused regression tests, full solution tests, and real CLI generation. + +### Parker: e-conomic equivalence implementation + +- Date: 2026-04-28T15:21:48.369+02:00 +- Decision: `OpenApiDocumentFactory.AreEquivalent()` should compare normalized OpenAPI/NSwag JSON first and use a narrow NJsonSchema structural fallback only for schemas whose standalone JSON serialization cannot resolve references. +- Rationale: duplicate schemas from separate vendor specs can be semantically identical while NJsonSchema object graphs contain unresolved/cyclic references; raw serializer comparison falsely reports conflicts. +- Guardrail: merge still copies missing keys, ignores equivalent duplicates, and throws `InvalidOperationException` for non-equivalent duplicate paths, schemas, definitions, and security schemes. +- Validation: focused merge tests and Issue1016 multi-spec regression tests passed. diff --git a/.squad/log/2026-04-28T15-21-48+02-00-economic-openapi-implementation.md b/.squad/log/2026-04-28T15-21-48+02-00-economic-openapi-implementation.md new file mode 100644 index 000000000..b1d0ec5ef --- /dev/null +++ b/.squad/log/2026-04-28T15-21-48+02-00-economic-openapi-implementation.md @@ -0,0 +1,12 @@ +# e-conomic OpenAPI implementation session + +- Timestamp: 2026-04-28T15:21:48.369+02:00 +- Pre-check: decisions.md size 25628 bytes; decision inbox files 3. +- Decisions archive gate: No archive entries eligible; decisions.md dated sections begin at 2026-04-21, newer than 30-day cutoff 2026-03-29. +- Decision inbox: merged 3 new entries and removed 3 inbox files. +- Implementation: Parker fixed canonical equivalent duplicate schema handling in OpenApiDocumentFactory. +- Validation: Lambert reported Release build, focused merge tests (15), Issue1016 regressions (7), full solution tests, format verification, and e-conomic CLI generation passed. +- Review: Ash approved the merge equivalence fix. +- Cross-agent histories: no append needed; Parker, Lambert, and Ash histories already contained the useful implementation, validation, and review updates. +- History summarization gate: no history.md files were >= 15360 bytes; none summarized. +- Health: decisions.md size before 25628 bytes, after 28115 bytes; inbox processed 3; histories summarized 0. diff --git a/.squad/orchestration-log/2026-04-28T15-21-48+02-00-ash.md b/.squad/orchestration-log/2026-04-28T15-21-48+02-00-ash.md new file mode 100644 index 000000000..ad48042c2 --- /dev/null +++ b/.squad/orchestration-log/2026-04-28T15-21-48+02-00-ash.md @@ -0,0 +1,8 @@ +# Ash orchestration log — e-conomic OpenAPI implementation + +- Timestamp: 2026-04-28T15:21:48.369+02:00 +- Role: Safety Reviewer +- Model: gpt-5.5 +- Work: Reviewed the canonical merge equivalence implementation and regression evidence. +- Verdict: APPROVED. +- Outcome: Fix preserves fail-fast conflict handling while accepting canonical duplicate equivalent schemas. diff --git a/.squad/orchestration-log/2026-04-28T15-21-48+02-00-lambert.md b/.squad/orchestration-log/2026-04-28T15-21-48+02-00-lambert.md new file mode 100644 index 000000000..0289967b3 --- /dev/null +++ b/.squad/orchestration-log/2026-04-28T15-21-48+02-00-lambert.md @@ -0,0 +1,8 @@ +# Lambert orchestration log — e-conomic OpenAPI implementation + +- Timestamp: 2026-04-28T15:21:48.369+02:00 +- Role: Tester +- Model: gpt-5.5 +- Work: Added e-conomic regression coverage in src\Refitter.Tests\RegressionTests\Issue1016_MultiSpecSchemaMergeTests.cs. +- Validation: Release build, focused merge tests, focused Issue1016 tests, full Release solution tests, format verification, and e-conomic CLI generation all passed. +- Outcome: Generated est\GeneratedCode\economic.cs from the real e-conomic settings file. diff --git a/.squad/orchestration-log/2026-04-28T15-21-48+02-00-parker.md b/.squad/orchestration-log/2026-04-28T15-21-48+02-00-parker.md new file mode 100644 index 000000000..4d46f507b --- /dev/null +++ b/.squad/orchestration-log/2026-04-28T15-21-48+02-00-parker.md @@ -0,0 +1,8 @@ +# Parker orchestration log — e-conomic OpenAPI implementation + +- Timestamp: 2026-04-28T15:21:48.369+02:00 +- Role: Core Dev +- Model: gpt-5.5 +- Work: Implemented canonical OpenAPI/NSwag/NJsonSchema merge equivalence in src\Refitter.Core\OpenApiDocumentFactory.cs. +- Tests: Added focused merge equivalence coverage in src\Refitter.Tests\OpenApiDocumentFactoryMergeTests.cs. +- Outcome: Equivalent duplicate schemas are accepted; genuine duplicate conflicts still fail fast. diff --git a/.squad/skills/multi-source-openapi-tooling/SKILL.md b/.squad/skills/multi-source-openapi-tooling/SKILL.md new file mode 100644 index 000000000..05fe8af13 --- /dev/null +++ b/.squad/skills/multi-source-openapi-tooling/SKILL.md @@ -0,0 +1,27 @@ +--- +name: "multi-source-openapi-tooling" +description: "Diagnose Refitter tooling behavior for .refitter files with openApiPaths" +domain: "tooling" +confidence: "high" +source: "earned" +last_updated: "2026-04-28T15:21:48.369+02:00" +--- + +## Context + +Use this when a `.refitter` file contains `openApiPaths` and a failure might come from settings parsing, path resolution, CLI/source-generator/MSBuild behavior, or core multi-document merge. + +## Pattern + +- First prove the settings file is accepted: `SettingsValidator` supports `openApiPaths`, rejects only simultaneous `openApiPath` + `openApiPaths`, applies settings-file defaults, and resolves relative entries against the `.refitter` file directory. +- Generate each OpenAPI spec independently through a scratch `.refitter` file before blaming path resolution; successful single-spec generation proves loading is fine. +- Run the original multi-spec `.refitter` with CLI `--simple-output` to see whether failure occurs before `GeneratedFile:` markers. +- Remember `--skip-validation` skips `OpenApiValidator` only; it does not bypass `RefitGenerator.CreateAsync(...)` document loading or merge. +- Source generator and MSBuild are downstream tooling surfaces: source generator calls `RefitGenerator.CreateAsync(settings)` directly, while MSBuild shells out to CLI and trusts `GeneratedFile:` markers. +- For duplicate schema failures, distinguish real conflicts from equivalent common schemas shared across specs. The core merge policy should fail fast for different shapes but allow semantically equivalent duplicates without requiring edits to vendor OpenAPI files. + +## Anti-patterns + +- Treating a core merge exception as a `.refitter` parse failure after settings validation has already reached generation. +- Expecting source-generator `outputFolder` to affect physical layout; source generator emits Roslyn hint names instead of disk files. +- Suggesting `--skip-validation` for document load/merge exceptions. diff --git a/src/Refitter.Core/OpenApiDocumentFactory.cs b/src/Refitter.Core/OpenApiDocumentFactory.cs index 86c6ab8a2..372e09cbd 100644 --- a/src/Refitter.Core/OpenApiDocumentFactory.cs +++ b/src/Refitter.Core/OpenApiDocumentFactory.cs @@ -1,7 +1,11 @@ using System.Diagnostics.CodeAnalysis; using System.Net; +using System.Runtime.CompilerServices; using Microsoft.OpenApi; using Microsoft.OpenApi.Reader; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NJsonSchema; using NSwag; using OpenApiDocument = NSwag.OpenApiDocument; @@ -55,13 +59,7 @@ public static async Task CreateAsync(IEnumerable openAp private static OpenApiDocument Merge(OpenApiDocument[] documents) { var baseDocument = OpenApiDocument.FromJsonAsync(documents[0].ToJson(documents[0].SchemaType)).GetAwaiter().GetResult(); - var tags = baseDocument.Tags; - HashSet? tagNames = null; - - if (tags != null) - { - tagNames = new HashSet(tags.Select(t => t.Name), StringComparer.Ordinal); - } + var tagNames = new HashSet(baseDocument.Tags.Select(t => t.Name), StringComparer.Ordinal); for (var i = 1; i < documents.Length; i++) { @@ -71,7 +69,7 @@ private static OpenApiDocument Merge(OpenApiDocument[] documents) MergeIfMissingOrThrowOnConflict(baseDocument.Paths, path.Key, path.Value, "path"); } - if (document.Components?.Schemas != null) + if (document.Components.Schemas.Count > 0) { foreach (var schema in document.Components.Schemas) { @@ -95,10 +93,8 @@ private static OpenApiDocument Merge(OpenApiDocument[] documents) } } - if (document.Tags != null) + if (document.Tags.Count > 0) { - baseDocument.Tags ??= []; - tagNames ??= new HashSet(baseDocument.Tags.Select(t => t.Name), StringComparer.Ordinal); foreach (var tag in document.Tags) { if (tagNames.Add(tag.Name)) @@ -133,7 +129,9 @@ private static bool AreEquivalent(TValue existingValue, TValue incomingV try { - return Serializer.Serialize(existingValue!) == Serializer.Serialize(incomingValue!); + return JToken.DeepEquals( + CreateCanonicalJsonToken(existingValue!), + CreateCanonicalJsonToken(incomingValue!)); } catch { @@ -141,6 +139,248 @@ private static bool AreEquivalent(TValue existingValue, TValue incomingV } } + private static JToken CreateCanonicalJsonToken(object value) + { + try + { + return NormalizeJsonToken(JToken.Parse(CreateOpenApiJson(value))); + } + catch when (value is JsonSchema schema) + { + return CreateCanonicalSchemaToken(schema, new HashSet(JsonSchemaReferenceComparer.Instance)); + } + } + + private static string CreateOpenApiJson(object value) + => value switch + { + OpenApiDocument document => document.ToJson(), + JsonSchema schema => CreateDocumentWithSchema(schema).ToJson(), + NSwag.OpenApiPathItem pathItem => CreateDocumentWithPath(pathItem).ToJson(), + NSwag.OpenApiSecurityScheme securityScheme => CreateDocumentWithSecurityScheme(securityScheme).ToJson(), + _ => JsonConvert.SerializeObject(value, Formatting.None) + }; + + private static OpenApiDocument CreateDocumentWithSchema(JsonSchema schema) + { + var document = CreateSerializationDocument(); + document.Definitions["Schema"] = schema; + AddReferencedSchemas(document.Definitions, schema); + return document; + } + + private static void AddReferencedSchemas(IDictionary definitions, JsonSchema schema) + { + var visited = new HashSet(JsonSchemaReferenceComparer.Instance); + var schemasToProcess = new Stack(); + schemasToProcess.Push(schema); + + while (schemasToProcess.Count > 0) + { + var schemaToProcess = schemasToProcess.Pop(); + var actualSchema = schemaToProcess.ActualSchema; + if (!visited.Add(actualSchema)) + continue; + + var definitionName = GetDefinitionName(schemaToProcess) ?? GetDefinitionName(actualSchema); + if (definitionName != null && !definitions.ContainsKey(definitionName)) + definitions.Add(definitionName, actualSchema); + + foreach (var childSchema in EnumerateTraversableSchemas(actualSchema)) + { + if (childSchema != null) + schemasToProcess.Push(childSchema); + } + } + } + + private static string? GetDefinitionName(JsonSchema schema) + { + var referencePath = ((NJsonSchema.References.IJsonReferenceBase)schema).ReferencePath; + if (string.IsNullOrWhiteSpace(referencePath)) + return null; + + var separatorIndex = referencePath!.LastIndexOf('/'); + return separatorIndex >= 0 && separatorIndex < referencePath.Length - 1 + ? Uri.UnescapeDataString(referencePath.Substring(separatorIndex + 1)) + : null; + } + + private static IEnumerable EnumerateTraversableSchemas(JsonSchema schema) + { + yield return schema.AdditionalItemsSchema; + yield return schema.AdditionalPropertiesSchema; + yield return schema.DictionaryKey; + yield return schema.Item; + + if (schema.Items != null) + { + foreach (var item in schema.Items) + { + yield return item; + } + } + + yield return schema.Not; + + foreach (var property in schema.Properties.Values) + { + yield return property; + } + + foreach (var subSchema in schema.AllOf) + { + yield return subSchema; + } + + foreach (var subSchema in schema.OneOf) + { + yield return subSchema; + } + + foreach (var subSchema in schema.AnyOf) + { + yield return subSchema; + } + + foreach (var definition in schema.Definitions.Values) + { + yield return definition; + } + } + + private static OpenApiDocument CreateDocumentWithPath(NSwag.OpenApiPathItem pathItem) + { + var document = CreateSerializationDocument(); + document.Paths["/_"] = pathItem; + return document; + } + + private static OpenApiDocument CreateDocumentWithSecurityScheme(NSwag.OpenApiSecurityScheme securityScheme) + { + var document = CreateSerializationDocument(); + document.SecurityDefinitions["SecurityScheme"] = securityScheme; + return document; + } + + private static OpenApiDocument CreateSerializationDocument() + => new() + { + Info = + { + Title = "Refitter equivalence comparison", + Version = "1.0" + } + }; + + private static JToken NormalizeJsonToken(JToken token) + => token switch + { + JObject jsonObject => new JObject( + jsonObject + .Properties() + .OrderBy(property => property.Name, StringComparer.Ordinal) + .Select(property => new JProperty(property.Name, NormalizeJsonToken(property.Value)))), + JArray jsonArray => new JArray(jsonArray.Select(NormalizeJsonToken)), + _ => token.DeepClone() + }; + + private static JToken CreateCanonicalSchemaToken(JsonSchema schema, ISet visited) + { + if (schema.Reference != null) + return CreateCanonicalSchemaReferenceToken(schema.Reference, visited); + + var actualSchema = schema.ActualSchema; + if (!visited.Add(actualSchema)) + return new JObject { ["$ref"] = "#" }; + + var json = new JObject + { + ["type"] = actualSchema.Type.ToString(), + ["format"] = actualSchema.Format, + ["title"] = actualSchema.Title, + ["description"] = actualSchema.Description, + ["nullable"] = actualSchema.IsNullableRaw, + ["allowAdditionalProperties"] = actualSchema.AllowAdditionalProperties + }; + + AddSchemaToken(json, "additionalProperties", actualSchema.AdditionalPropertiesSchema, visited); + AddSchemaToken(json, "items", actualSchema.Item, visited); + AddSchemaArray(json, "allOf", actualSchema.AllOf, visited); + AddSchemaArray(json, "oneOf", actualSchema.OneOf, visited); + AddSchemaArray(json, "anyOf", actualSchema.AnyOf, visited); + + if (actualSchema.RequiredProperties.Count > 0) + json["required"] = new JArray(actualSchema.RequiredProperties.OrderBy(name => name, StringComparer.Ordinal)); + + if (actualSchema.Properties.Count > 0) + { + json["properties"] = new JObject( + actualSchema.Properties + .OrderBy(property => property.Key, StringComparer.Ordinal) + .Select(property => new JProperty( + property.Key, + CreateCanonicalSchemaToken(property.Value, new HashSet(visited, JsonSchemaReferenceComparer.Instance))))); + } + + if (actualSchema.Enumeration.Count > 0) + json["enum"] = new JArray(actualSchema.Enumeration.Select(value => value != null ? JToken.FromObject(value) : JValue.CreateNull())); + + if (actualSchema.ExtensionData is { Count: > 0 }) + { + json["extensions"] = new JObject( + actualSchema.ExtensionData + .OrderBy(extension => extension.Key, StringComparer.Ordinal) + .Select(extension => new JProperty( + extension.Key, + extension.Value != null ? NormalizeJsonToken(JToken.FromObject(extension.Value)) : JValue.CreateNull()))); + } + + return RemoveNullProperties(json); + } + + private static JToken CreateCanonicalSchemaReferenceToken(JsonSchema reference, ISet visited) => + visited.Contains(reference) + ? new JObject { ["$ref"] = "#" } + : CreateCanonicalSchemaToken(reference, new HashSet(visited, JsonSchemaReferenceComparer.Instance)); + + private static void AddSchemaToken(JObject json, string propertyName, JsonSchema? schema, ISet visited) + { + if (schema != null) + json[propertyName] = CreateCanonicalSchemaToken(schema, new HashSet(visited, JsonSchemaReferenceComparer.Instance)); + } + + private static void AddSchemaArray(JObject json, string propertyName, IEnumerable schemas, ISet visited) + { + var items = schemas + .Select(schema => CreateCanonicalSchemaToken(schema, new HashSet(visited, JsonSchemaReferenceComparer.Instance))) + .ToArray(); + + if (items.Length > 0) + json[propertyName] = new JArray(items); + } + + private static JObject RemoveNullProperties(JObject json) + { + foreach (var property in json.Properties().Where(property => property.Value.Type == JTokenType.Null).ToArray()) + { + property.Remove(); + } + + return json; + } + + private sealed class JsonSchemaReferenceComparer : IEqualityComparer + { + public static JsonSchemaReferenceComparer Instance { get; } = new(); + + public bool Equals(JsonSchema? x, JsonSchema? y) => + ReferenceEquals(x, y); + + public int GetHashCode(JsonSchema obj) => + RuntimeHelpers.GetHashCode(obj); + } + private static InvalidOperationException CreateMergeConflictException(string itemType, string key) => new($"Cannot merge OpenAPI documents because a duplicate {itemType} '{key}' was found. Refitter fails fast on merge collisions to avoid silent data loss."); diff --git a/src/Refitter.Tests/OpenApiDocumentFactoryMergeTests.cs b/src/Refitter.Tests/OpenApiDocumentFactoryMergeTests.cs index d30832e88..cfd33a024 100644 --- a/src/Refitter.Tests/OpenApiDocumentFactoryMergeTests.cs +++ b/src/Refitter.Tests/OpenApiDocumentFactoryMergeTests.cs @@ -1,5 +1,6 @@ using System.Reflection; using FluentAssertions; +using Newtonsoft.Json.Linq; using NJsonSchema; using NSwag; using Refitter.Core; @@ -529,6 +530,151 @@ public async Task Merge_For_NonConflicting_Documents_Returns_A_New_Document_With secondDocument.Tags.Should().ContainSingle().Which.Name.Should().Be("orders"); } + [Test] + public async Task Merge_With_Equivalent_Recursive_Schema_Duplicate_Ignores_Duplicate() + { + const string baseSpec = """ + { + "openapi": "3.0.0", + "info": { + "title": "Base API", + "version": "1.0" + }, + "paths": { + "/nodes": { + "get": { + "operationId": "ListNodes", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Node" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Node": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "next": { + "$ref": "#/components/schemas/Node" + } + } + } + } + } + } + """; + + const string secondSpec = """ + { + "openapi": "3.0.0", + "info": { + "title": "Second API", + "version": "1.0" + }, + "paths": { + "/linked-list": { + "get": { + "operationId": "GetLinkedList", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Node" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Node": { + "properties": { + "next": { + "$ref": "#/components/schemas/Node" + }, + "id": { + "type": "string" + } + }, + "type": "object" + } + } + } + } + """; + + var baseDocument = await ParseJsonDocument(baseSpec); + var secondDocument = await ParseJsonDocument(secondSpec); + + var merged = InvokeMerge(baseDocument, secondDocument); + + merged.Paths.Should().ContainKeys("/nodes", "/linked-list"); + merged.Components.Schemas.Should().ContainSingle().Which.Key.Should().Be("Node"); + baseDocument.Paths.Should().ContainSingle().Which.Key.Should().Be("/nodes"); + secondDocument.Paths.Should().ContainSingle().Which.Key.Should().Be("/linked-list"); + } + + [Test] + public async Task Merge_With_Equivalent_Swagger2_Definition_Duplicate_Ignores_Duplicate() + { + var target = new Dictionary + { + ["ProblemDetails"] = await JsonSchema.FromJsonAsync(""" + { + "type": "object", + "required": [ "title" ], + "properties": { + "title": { + "type": "string" + }, + "status": { + "type": "integer", + "format": "int32" + } + } + } + """) + }; + var incoming = await JsonSchema.FromJsonAsync(""" + { + "properties": { + "status": { + "format": "int32", + "type": "integer" + }, + "title": { + "type": "string" + } + }, + "required": [ "title" ], + "type": "object" + } + """); + + InvokeMergeIfMissingOrThrowOnConflict(target, "ProblemDetails", incoming, "definition"); + + target.Should().ContainSingle().Which.Key.Should().Be("ProblemDetails"); + target["ProblemDetails"].Properties.Should().ContainKeys("title", "status"); + } + [Test] public async Task Merge_With_Collisions_Throws_And_Does_Not_Mutate_Inputs() { @@ -816,6 +962,537 @@ public async Task Merge_With_Security_Scheme_Collision_Throws_And_Does_Not_Mutat secondDocument.SecurityDefinitions["ApiKey"].Name.Should().Be("X-Second-Key"); } + [Test] + public async Task Merge_With_Equivalent_Composed_Schema_Duplicate_Ignores_Duplicate() + { + const string schemaJson = """ + { + "type": "object", + "required": [ "child", "kind" ], + "properties": { + "kind": { + "type": "string", + "enum": [ "basic", "advanced" ] + }, + "child": { + "$ref": "#/definitions/NamedChild" + } + }, + "allOf": [ + { + "$ref": "#/definitions/BasePart" + } + ], + "oneOf": [ + { + "$ref": "#/definitions/NamedChild" + }, + { + "type": "string" + } + ], + "anyOf": [ + { + "type": "integer" + }, + { + "$ref": "#/definitions/BasePart" + } + ], + "x-meta": { + "alpha": 1, + "zeta": null + }, + "x-null": null, + "definitions": { + "NamedChild": { + "type": "object", + "required": [ "id" ], + "properties": { + "id": { + "type": "string" + } + } + }, + "BasePart": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + } + } + } + } + """; + var target = new Dictionary + { + ["Shared"] = await JsonSchema.FromJsonAsync(schemaJson) + }; + var incoming = await JsonSchema.FromJsonAsync(schemaJson); + + InvokeMergeIfMissingOrThrowOnConflict(target, "Shared", incoming, "schema"); + + target.Should().ContainSingle().Which.Key.Should().Be("Shared"); + target["Shared"].RequiredProperties.Should().BeEquivalentTo(["kind", "child"]); + target["Shared"].Properties.Should().ContainKeys("kind", "child"); + target["Shared"].Properties["kind"].Enumeration.Should().BeEquivalentTo(["basic", "advanced"]); + } + + [Test] + public async Task Merge_With_Base_Tags_Null_And_Swagger2_Only_Incoming_Document_Merges_Without_Creating_Tags() + { + const string baseSpec = """ + { + "openapi": "3.0.0", + "info": { + "title": "Base API", + "version": "1.0" + }, + "paths": { + "/users": { + "get": { + "operationId": "ListUsers", + "responses": { + "200": { + "description": "Success" + } + } + } + } + } + } + """; + + const string swagger2IncomingSpec = """ + { + "swagger": "2.0", + "info": { + "title": "Legacy API", + "version": "1.0" + }, + "paths": { + "/orders": { + "get": { + "operationId": "ListOrders", + "responses": { + "200": { + "description": "Success" + } + } + } + } + }, + "definitions": { + "LegacyOrder": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + } + } + """; + + var baseDocument = await ParseJsonDocument(baseSpec); + var swagger2IncomingDocument = await ParseJsonDocument(swagger2IncomingSpec); + + var merged = InvokeMerge(baseDocument, swagger2IncomingDocument); + + merged.Tags.Should().BeEmpty(); + merged.Paths.Should().ContainKeys("/users", "/orders"); + merged.Definitions.Should().ContainKey("LegacyOrder"); + baseDocument.Tags.Should().BeEmpty(); + baseDocument.Definitions.Should().BeEmpty(); + } + + [Test] + public void CreateCanonicalSchemaToken_Emits_Complex_Schema_Shape_Without_Nulls() + { + var schema = new JsonSchema + { + Type = JsonObjectType.Object, + AllowAdditionalProperties = false, + AdditionalPropertiesSchema = new JsonSchema + { + Type = JsonObjectType.String + }, + Item = new JsonSchema + { + Type = JsonObjectType.Integer + }, + ExtensionData = new Dictionary + { + ["x-meta"] = new Dictionary + { + ["beta"] = 2, + ["alpha"] = 1 + } + } + }; + schema.RequiredProperties.Add("name"); + schema.Properties["name"] = new JsonSchemaProperty + { + Type = JsonObjectType.String + }; + schema.AllOf.Add(new JsonSchema { Type = JsonObjectType.String }); + schema.OneOf.Add(new JsonSchema { Type = JsonObjectType.Integer }); + schema.AnyOf.Add(new JsonSchema { Type = JsonObjectType.Boolean }); + schema.Enumeration.Add("active"); + schema.Enumeration.Add(null); + + var token = InvokeCreateCanonicalSchemaToken(schema, new HashSet()); + + token.Should().BeOfType(); + var json = (JObject)token; + json.Should().ContainKey("type"); + json["type"]!.Value().Should().Be("Object"); + json.Should().ContainKey("allowAdditionalProperties"); + json["allowAdditionalProperties"]!.Type.Should().Be(JTokenType.Boolean); + json.Should().ContainKey("additionalProperties"); + json["additionalProperties"]!["type"]!.Value().Should().Be("String"); + json.Should().ContainKey("items"); + json["items"]!["type"]!.Value().Should().Be("Integer"); + json.Should().ContainKey("allOf"); + json.Should().ContainKey("oneOf"); + json.Should().ContainKey("anyOf"); + json.Should().ContainKey("required"); + json["required"]!.Should().BeOfType().Which.Select(value => value!.Value()).Should().ContainSingle().Which.Should().Be("name"); + json.Should().ContainKey("properties"); + json["properties"]!["name"]!["type"]!.Value().Should().Be("String"); + json.Should().ContainKey("enum"); + json["enum"]!.Should().BeOfType().Which.Should().HaveCount(2); + json.Should().ContainKey("extensions"); + json["extensions"]!["x-meta"]!["alpha"]!.Value().Should().Be(1); + } + + [Test] + public void CreateCanonicalJsonToken_Falls_Back_To_Schema_Canonicalization_For_ReferenceSchema() + { + var schema = new JsonSchema + { + Reference = new JsonSchema + { + Type = JsonObjectType.String + } + }; + + var token = InvokeCreateCanonicalJsonToken(schema); + + token["type"]!.Value().Should().Be("String"); + } + + [Test] + public void CreateCanonicalSchemaToken_For_Visited_Reference_Uses_Placeholder() + { + var referencedSchema = new JsonSchema + { + Type = JsonObjectType.Object + }; + var schema = new JsonSchema + { + Reference = referencedSchema + }; + + var token = InvokeCreateCanonicalSchemaToken(schema, new HashSet { referencedSchema }); + + token.Should().BeOfType(); + ((JObject)token).Properties().Should().ContainSingle(); + token["$ref"]!.Value().Should().Be("#"); + } + + [Test] + public void CreateCanonicalSchemaToken_For_Unvisited_Reference_Canonicalizes_Referenced_Schema() + { + var referencedSchema = new JsonSchema + { + Type = JsonObjectType.Object + }; + var schema = new JsonSchema + { + Reference = referencedSchema + }; + + var token = InvokeCreateCanonicalSchemaToken(schema, new HashSet()); + + token["type"]!.Value().Should().Be("Object"); + } + + [Test] + public void CreateCanonicalSchemaToken_When_ActualSchema_Is_Already_Visited_Uses_Placeholder() + { + var schema = new JsonSchema + { + Type = JsonObjectType.Object + }; + + var token = InvokeCreateCanonicalSchemaToken(schema, new HashSet { schema }); + + token["$ref"]!.Value().Should().Be("#"); + } + + [Test] + public void GetDefinitionName_Decodes_Url_Encoded_Names() + { + var schema = new JsonSchema(); + ((NJsonSchema.References.IJsonReferenceBase)schema).ReferencePath = "#/definitions/Problem%20Details"; + + InvokeGetDefinitionName(schema).Should().Be("Problem Details"); + } + + [Test] + public void GetDefinitionName_Without_Path_Separator_Returns_Null() + { + var schema = new JsonSchema(); + ((NJsonSchema.References.IJsonReferenceBase)schema).ReferencePath = "ProblemDetails"; + + InvokeGetDefinitionName(schema).Should().BeNull(); + } + + [Test] + public async Task CreateOpenApiJson_Serializes_OpenApiDocuments_And_Pocos() + { + var document = await ParseJsonDocument(""" + { + "openapi": "3.0.0", + "info": { + "title": "Coverage API", + "version": "1.0" + }, + "paths": { + "/coverage": { + "get": { + "operationId": "GetCoverage", + "responses": { + "200": { + "description": "Success" + } + } + } + } + } + } + """); + + InvokeCreateOpenApiJson(document).Should().Contain("\"/coverage\""); + InvokeCreateOpenApiJson(new { Name = "Lambert", Count = 2 }) + .Should() + .Be("""{"Name":"Lambert","Count":2}"""); + } + + [Test] + public void AreEquivalent_Returns_False_When_Canonicalization_Throws() + { + var left = new SelfReferencingValue(); + left.Self = left; + + var right = new SelfReferencingValue(); + right.Self = right; + + InvokeAreEquivalent(left, right).Should().BeFalse(); + } + + [Test] + public void RemoveNullProperties_Removes_Only_Null_Values() + { + var json = new JObject + { + ["type"] = "object", + ["format"] = JValue.CreateNull(), + ["description"] = JValue.CreateNull(), + ["properties"] = new JObject() + }; + + var normalized = InvokeRemoveNullProperties(json); + + normalized.Should().BeSameAs(json); + normalized.Should().ContainKey("type"); + normalized.Should().ContainKey("properties"); + normalized.Should().NotContainKeys("format", "description"); + } + + [Test] + public async Task AddReferencedSchemas_Does_Not_Overwrite_Existing_Named_Schema() + { + var rootSchema = new JsonSchema + { + Type = JsonObjectType.Object + }; + var referencedChildSchema = await JsonSchema.FromJsonAsync(""" + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + """); + var referencedChild = new JsonSchemaProperty + { + Reference = referencedChildSchema + }; + ((NJsonSchema.References.IJsonReferenceBase)referencedChild).ReferencePath = "#/definitions/NamedChild"; + rootSchema.Properties["child"] = referencedChild; + + var existingDefinition = new JsonSchema + { + Type = JsonObjectType.Integer + }; + var definitions = new Dictionary + { + ["NamedChild"] = existingDefinition + }; + + InvokeAddReferencedSchemas(definitions, rootSchema); + + definitions.Should().ContainSingle().Which.Value.Should().BeSameAs(existingDefinition); + } + + [Test] + public async Task AddReferencedSchemas_Adds_Named_Property_Schema_To_Empty_Definitions() + { + var rootSchema = new JsonSchema + { + Type = JsonObjectType.Object + }; + var referencedChildSchema = await JsonSchema.FromJsonAsync(""" + { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + } + """); + var referencedChild = new JsonSchemaProperty + { + Reference = referencedChildSchema + }; + ((NJsonSchema.References.IJsonReferenceBase)referencedChild).ReferencePath = "#/definitions/NamedChild"; + rootSchema.Properties["child"] = referencedChild; + + var definitions = new Dictionary(); + + InvokeAddReferencedSchemas(definitions, rootSchema); + + definitions.Should().ContainSingle().Which.Key.Should().Be("NamedChild"); + definitions["NamedChild"].Should().BeSameAs(referencedChildSchema); + } + + [Test] + public void CreateCanonicalSchemaToken_Preserves_Null_And_NonNull_Extension_Values() + { + var schema = new JsonSchema + { + Type = JsonObjectType.Object, + ExtensionData = new Dictionary + { + ["x-null"] = null, + ["x-meta"] = new Dictionary + { + ["beta"] = 2 + } + } + }; + + var token = InvokeCreateCanonicalSchemaToken(schema, new HashSet()); + + token["extensions"]!["x-null"]!.Type.Should().Be(JTokenType.Null); + token["extensions"]!["x-meta"]!["beta"]!.Value().Should().Be(2); + } + + [Test] + public void AddReferencedSchemas_Adds_Named_Item_Schemas_From_Items_Collection() + { + var rootSchema = new JsonSchema + { + Type = JsonObjectType.Array + }; + var referencedItemSchema = new JsonSchema + { + Type = JsonObjectType.String + }; + var namedItemSchema = new JsonSchemaProperty + { + Reference = referencedItemSchema + }; + ((NJsonSchema.References.IJsonReferenceBase)namedItemSchema).ReferencePath = "#/definitions/NamedItem"; + rootSchema.Items.Add(namedItemSchema); + + var definitions = new Dictionary(); + + InvokeAddReferencedSchemas(definitions, rootSchema); + + definitions.Should().ContainSingle().Which.Key.Should().Be("NamedItem"); + definitions["NamedItem"].Should().BeSameAs(referencedItemSchema); + } + + [Test] + public async Task ParseJsonDocument_Normalizes_Missing_Components_Schemas_To_Empty_Collections() + { + var document = await ParseJsonDocument(""" + { + "openapi": "3.0.0", + "info": { + "title": "Components Coverage API", + "version": "1.0" + }, + "paths": { + "/orders": { + "get": { + "operationId": "ListOrders", + "responses": { + "200": { + "description": "Success" + } + } + } + } + } + } + """); + + document.Components.Should().NotBeNull(); + document.Components.Schemas.Should().NotBeNull(); + document.Components.Schemas.Should().BeEmpty(); + } + + [Test] + public async Task ParseAndCloneDocument_Normalize_Missing_Tags_To_Empty_Collections() + { + var parsedDocument = await ParseJsonDocument(""" + { + "openapi": "3.0.0", + "info": { + "title": "Tag Coverage API", + "version": "1.0" + }, + "paths": { + "/users": { + "get": { + "operationId": "ListUsers", + "responses": { + "200": { + "description": "Success" + } + } + } + } + } + } + """); + var clonedDocument = await OpenApiDocument.FromJsonAsync(parsedDocument.ToJson()); + + parsedDocument.Tags.Should().NotBeNull(); + parsedDocument.Tags.Should().BeEmpty(); + clonedDocument.Tags.Should().NotBeNull(); + clonedDocument.Tags.Should().BeEmpty(); + } + private static Task ParseJsonDocument(string json) => OpenApiDocument.FromJsonAsync(json); @@ -854,4 +1531,66 @@ private static void InvokeMergeIfMissingOrThrowOnConflict( throw exception.InnerException; } } + + private static bool InvokeAreEquivalent(TValue existingValue, TValue incomingValue) + { + var areEquivalentMethod = typeof(OpenApiDocumentFactory) + .GetMethod("AreEquivalent", BindingFlags.NonPublic | BindingFlags.Static)! + .MakeGenericMethod(typeof(TValue)); + + return (bool)areEquivalentMethod.Invoke(null, [existingValue!, incomingValue!])!; + } + + private static JToken InvokeCreateCanonicalSchemaToken(JsonSchema schema, ISet visited) + { + var createCanonicalSchemaTokenMethod = typeof(OpenApiDocumentFactory) + .GetMethod("CreateCanonicalSchemaToken", BindingFlags.NonPublic | BindingFlags.Static)!; + + return (JToken)createCanonicalSchemaTokenMethod.Invoke(null, [schema, visited])!; + } + + private static JToken InvokeCreateCanonicalJsonToken(object value) + { + var createCanonicalJsonTokenMethod = typeof(OpenApiDocumentFactory) + .GetMethod("CreateCanonicalJsonToken", BindingFlags.NonPublic | BindingFlags.Static)!; + + return (JToken)createCanonicalJsonTokenMethod.Invoke(null, [value])!; + } + + private static void InvokeAddReferencedSchemas(IDictionary definitions, JsonSchema schema) + { + var addReferencedSchemasMethod = typeof(OpenApiDocumentFactory) + .GetMethod("AddReferencedSchemas", BindingFlags.NonPublic | BindingFlags.Static)!; + + addReferencedSchemasMethod.Invoke(null, [definitions, schema]); + } + + private static string? InvokeGetDefinitionName(JsonSchema schema) + { + var getDefinitionNameMethod = typeof(OpenApiDocumentFactory) + .GetMethod("GetDefinitionName", BindingFlags.NonPublic | BindingFlags.Static)!; + + return (string?)getDefinitionNameMethod.Invoke(null, [schema]); + } + + private static string InvokeCreateOpenApiJson(object value) + { + var createOpenApiJsonMethod = typeof(OpenApiDocumentFactory) + .GetMethod("CreateOpenApiJson", BindingFlags.NonPublic | BindingFlags.Static)!; + + return (string)createOpenApiJsonMethod.Invoke(null, [value])!; + } + + private static JObject InvokeRemoveNullProperties(JObject json) + { + var removeNullPropertiesMethod = typeof(OpenApiDocumentFactory) + .GetMethod("RemoveNullProperties", BindingFlags.NonPublic | BindingFlags.Static)!; + + return (JObject)removeNullPropertiesMethod.Invoke(null, [json])!; + } + + private sealed class SelfReferencingValue + { + public SelfReferencingValue? Self { get; set; } + } } diff --git a/src/Refitter.Tests/RegressionTests/Issue1013_ContractSuffixCorruptionTests.cs b/src/Refitter.Tests/RegressionTests/ContractSuffixCorruptionTests.cs similarity index 99% rename from src/Refitter.Tests/RegressionTests/Issue1013_ContractSuffixCorruptionTests.cs rename to src/Refitter.Tests/RegressionTests/ContractSuffixCorruptionTests.cs index c239ba697..635cc211a 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1013_ContractSuffixCorruptionTests.cs +++ b/src/Refitter.Tests/RegressionTests/ContractSuffixCorruptionTests.cs @@ -10,7 +10,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1013: ContractTypeSuffix regex corruption /// Validates that suffix replacement doesn't corrupt XML comments, string literals, or partial name matches /// -public class Issue1013_ContractSuffixCorruptionTests +public class ContractSuffixCorruptionTests { private const string OpenApiSpec = @" { diff --git a/src/Refitter.Tests/RegressionTests/Issue1039_DynamicQuerystringMutationTests.cs b/src/Refitter.Tests/RegressionTests/DynamicQuerystringMutationTests.cs similarity index 98% rename from src/Refitter.Tests/RegressionTests/Issue1039_DynamicQuerystringMutationTests.cs rename to src/Refitter.Tests/RegressionTests/DynamicQuerystringMutationTests.cs index fa27305fb..22555af86 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1039_DynamicQuerystringMutationTests.cs +++ b/src/Refitter.Tests/RegressionTests/DynamicQuerystringMutationTests.cs @@ -8,7 +8,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1039: dynamic querystring extraction mutates the shared NSwag model. /// Validates that XML documentation still sees the original query parameters after wrapper generation. /// -public class Issue1039_DynamicQuerystringMutationTests +public class DynamicQuerystringMutationTests { private const string OpenApiSpec = """ { diff --git a/src/Refitter.Tests/RegressionTests/Issue1014_EnumConverterInjectionTests.cs b/src/Refitter.Tests/RegressionTests/EnumConverterInjectionTests.cs similarity index 99% rename from src/Refitter.Tests/RegressionTests/Issue1014_EnumConverterInjectionTests.cs rename to src/Refitter.Tests/RegressionTests/EnumConverterInjectionTests.cs index 76c1e2d75..8663be81a 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1014_EnumConverterInjectionTests.cs +++ b/src/Refitter.Tests/RegressionTests/EnumConverterInjectionTests.cs @@ -10,7 +10,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1014: Internal enum JsonConverter injection /// Validates that both internal and public enums receive JsonConverter attributes /// -public class Issue1014_EnumConverterInjectionTests +public class EnumConverterInjectionTests { private const string OpenApiSpec = @" { diff --git a/src/Refitter.Tests/RegressionTests/Issue1051_MalformedUnicodeEscapeTests.cs b/src/Refitter.Tests/RegressionTests/MalformedUnicodeEscapeTests.cs similarity index 99% rename from src/Refitter.Tests/RegressionTests/Issue1051_MalformedUnicodeEscapeTests.cs rename to src/Refitter.Tests/RegressionTests/MalformedUnicodeEscapeTests.cs index 1f6c5ad7f..7d82c7704 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1051_MalformedUnicodeEscapeTests.cs +++ b/src/Refitter.Tests/RegressionTests/MalformedUnicodeEscapeTests.cs @@ -8,7 +8,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1051: XmlDocumentationGenerator.DecodeJsonEscapedText mishandles malformed \u sequences /// Validates that malformed Unicode escape sequences (e.g., \uZZZZ) are handled correctly without consuming extra characters /// -public class Issue1051_MalformedUnicodeEscapeTests +public class MalformedUnicodeEscapeTests { [Test] public void DecodeJsonEscapedText_Handles_Valid_Unicode_Escape() diff --git a/src/Refitter.Tests/RegressionTests/Issue1016_MultiSpecSchemaMergeTests.cs b/src/Refitter.Tests/RegressionTests/MultiSpecSchemaMergeTests.cs similarity index 84% rename from src/Refitter.Tests/RegressionTests/Issue1016_MultiSpecSchemaMergeTests.cs rename to src/Refitter.Tests/RegressionTests/MultiSpecSchemaMergeTests.cs index 478a3ca09..e25db6283 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1016_MultiSpecSchemaMergeTests.cs +++ b/src/Refitter.Tests/RegressionTests/MultiSpecSchemaMergeTests.cs @@ -9,7 +9,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1016: Multi-spec merge drops schemas /// Validates that schemas from all OpenAPI specs are preserved during merge /// -public class Issue1016_MultiSpecSchemaMergeTests +public class MultiSpecSchemaMergeTests { // First spec: has paths but NO components section private const string SpecNoComponents = @" @@ -240,6 +240,28 @@ public async Task Can_Build_Merged_Code_From_Multiple_Specs() .BeTrue("merged code from multiple specs should compile"); } + [Test] + public async Task Economic_OpenApiPaths_Generate_NonEmpty_Buildable_Code() + { + var repositoryRoot = GetRepositoryRoot(); + var productsSpec = Path.Combine(repositoryRoot, "test", "OpenAPI", "v3.0", "economic-products.json"); + var webhooksSpec = Path.Combine(repositoryRoot, "test", "OpenAPI", "v3.0", "economic-webhooks.json"); + var settings = new RefitGeneratorSettings + { + OpenApiPaths = [productsSpec, webhooksSpec], + Namespace = "Economic" + }; + + var sut = await RefitGenerator.CreateAsync(settings); + var generatedCode = sut.Generate(); + + generatedCode.Should().NotBeNullOrWhiteSpace(); + BuildHelper + .BuildCSharp(generatedCode) + .Should() + .BeTrue("the real e-conomic multi-spec output should compile"); + } + private static async Task<(string file1, string file2)> CreateSpecFiles(string spec1, string spec2) { var file1 = await TestFile.CreateSwaggerFile(spec1, "spec1.json"); @@ -266,4 +288,16 @@ private static async Task GenerateCodeFromMultipleSpecs(params string[] var generatedCode = sut.Generate(); return generatedCode; } + + private static string GetRepositoryRoot() + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "README.md"))) + { + directory = directory.Parent; + } + + directory.Should().NotBeNull("tests should run from within the repository workspace"); + return directory!.FullName; + } } diff --git a/src/Refitter.Tests/RegressionTests/Issue1015_OneOfDiscriminatorNullRefTests.cs b/src/Refitter.Tests/RegressionTests/OneOfDiscriminatorNullRefTests.cs similarity index 99% rename from src/Refitter.Tests/RegressionTests/Issue1015_OneOfDiscriminatorNullRefTests.cs rename to src/Refitter.Tests/RegressionTests/OneOfDiscriminatorNullRefTests.cs index c2dac5af9..22b172933 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1015_OneOfDiscriminatorNullRefTests.cs +++ b/src/Refitter.Tests/RegressionTests/OneOfDiscriminatorNullRefTests.cs @@ -10,7 +10,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1015: NullReferenceException on Swagger 2.0 documents /// Validates graceful handling when document.Components is null or document.Components.Schemas is null /// -public class Issue1015_OneOfDiscriminatorNullRefTests +public class OneOfDiscriminatorNullRefTests { // Swagger 2.0 uses "definitions" not "components" private const string Swagger20Spec = @" diff --git a/src/Refitter.Tests/RegressionTests/Issue1035_XmlDocEscapingTests.cs b/src/Refitter.Tests/RegressionTests/XmlDocEscapingTests.cs similarity index 99% rename from src/Refitter.Tests/RegressionTests/Issue1035_XmlDocEscapingTests.cs rename to src/Refitter.Tests/RegressionTests/XmlDocEscapingTests.cs index 2be4b4a5d..80506cb75 100644 --- a/src/Refitter.Tests/RegressionTests/Issue1035_XmlDocEscapingTests.cs +++ b/src/Refitter.Tests/RegressionTests/XmlDocEscapingTests.cs @@ -10,7 +10,7 @@ namespace Refitter.Tests.RegressionTests; /// Regression tests for Issue #1035: XML doc emission does not escape user-supplied parameter / dynamic-querystring descriptions /// Validates that parameter descriptions containing XML-unsafe characters (&, <, >) are properly escaped in generated XML documentation /// -public class Issue1035_XmlDocEscapingTests +public class XmlDocEscapingTests { private const string SpecWithUnsafeParameterDescription = @" { diff --git a/test/OpenAPI/v3.0/economic-products.json b/test/OpenAPI/v3.0/economic-products.json new file mode 100644 index 000000000..b21aafe17 --- /dev/null +++ b/test/OpenAPI/v3.0/economic-products.json @@ -0,0 +1,6150 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Visma e-conomic OpenAPI Products", + "description": "# Changelog\n
\nClick to see changelog.\n\n| Version | Description | Date |\n|:--------|:------------|:-----|\n| V2.0.0 | Added [`Products`](#tag/Products) endpoints, updated [`ProductSpecialPricesInCurrency`](#tag/ProductSpecialPricesInCurrency) endpoint routes for flattened subcollections | January, 2026 |\n| V1.1.1 | Updated documentation | April, 2025 |\n| V1.1.0 | Added count endpoint to [`ProductGroups`](#tag/ProductGroups) and [`Zones`](#tag/Zones) | September, 2024 |\n| V1.0.0 | Added endpoints
  • Product Groups
    • [`ProductGroups`](#tag/ProductGroups)
    • [`AccrualSettings`](#tag/AccrualSettings)
    • [`ProjectSettings`](#tag/ProjectSettings)
    • [`Zones`](#tag/Zones)
  • Product Sales Price In Currencies
    • [`ProductSalesPriceInCurrencies`](#tag/ProductSalesPriceInCurrencies)
  • Price Groups
    • [`PriceGroups`](#tag/PriceGroups)
    • [`ProductSpecialPrices`](#tag/ProductSpecialPrices)
    • [`ProductSpecialPricesInCurrency`](#tag/ProductSpecialPricesInCurrency)
| August, 2024 |\n\n
\n\n# TL;DR\n\n**Create tokens as instructed [here](https://www.e-conomic.com/developer/connect) and then add these three headers to your requests:**\n\n| Header | Value | What is this? |\n| :-------------------- | :------------------------- | :----------------------------------------------------------- |\n| X-AppSecretToken | YOUR_PRIVATE_TOKEN | This identifies your app. This is your secret token. Try using the value `demo`. |\n| X-AgreementGrantToken | YOUR_AGREEMENT_GRANT_TOKEN | This identifies the grant issued by an agreement, to allow your app to access the agreements data. Try using the value `demo`. |\n| Content-Type | application/json | We’re a JSON based API. This tells us that you agree with us on using JSON. |\n\n**Optional headers:**\n\n| Header | Value | What is this? |\n| :-------------------- | :------------------------- | :----------------------------------------------------------- |\n| Idempotency-Key | YOUR_IDEMPOTENCY_KEY | This represents your own unique idempotency key. Enables you to make use of our [Idempotency Tokens](#section/Retrieving-data/Idempotency-tokens) feature. You can't use this feature with GET requests. |\n\n### Examples\n\n#### jQuery\n\n```javascript/jQuery\n$.ajax({\n url: \"https://apis.e-conomic.com/productsapi/v2.0.0/the_resource\",\n dataType: \"json\",\n headers: {\n 'X-AppSecretToken': \"demo\",\n 'X-AgreementGrantToken': \"demo\",\n 'Accept': \"application/json\"\n },\n type: \"GET\"\n})\n .always(function (data) {\n $(\"#output\").text(JSON.stringify(data, null, 4));\n});\n```\n\n#### cURL\n\n```curl\ncurl -H \"X-AppSecretToken: demo\" -H \"X-AgreementGrantToken: demo\" https://apis.e-conomic.com/productsapi/v2.0.0/the_resource\n```\n\n\n\n# Introduction\n\nWelcome to the **Visma e-conomic OpenAPI** documentation!\n\nThe e-conomic API is a document-based JSON REST API. \n\nFor more in-depth information about e-conomic itself, please have a look at the e-copedia [http://wiki.e-conomic.dk](http://wiki.e-conomic.dk/).\n\n## Usage\n\n- **Generating a client** can easily be done using tools like [swagger-codegen](https://github.com/swagger-api/swagger-codegen) or others that work with [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification) specs.\n\n\n## Versioning\n\nAPI releases are versioned using a three-part versioning scheme: `{major version}.{minor version}.{patch version}`.\n\nWe broadly follow [Semantic Versioning](https://semver.org/) principles when versioning the API. The major version number is incremented when a breaking change occurs. \n\nThe format is:\n\n`/{resource-api}/v{major version}.{minor version}.{patch version}/{resource-name}`\n\nEach value of the above are integers and you should configure the specific version in each API call. \n\nAn example could be: `/subscriptionsapi/v1.0.0/subscriptions`\n\nTo track the changes of versions, please see our [changelog](#section/Changelog).\n\nWe reserve the right to deprecate versions at intervals since this allows for moving into a friendly environment for you faster.\n\n## Demo authentication\n\nIf you wish to try out the API before registering a developer agreement, you can do this by using the demo agreement, which mimics the authentication flow you will have to use when you create your app. All you have to do is specify HTTP header tokens `X-AgreementGrantToken: demo` and `X-AppSecretToken: demo`. Note however that you can only do GET requests with the demo agreement. If you want full access to our API's, you will need to register.\n\n# Retrieving data\n\nOur data is exposed as collections of items. Each item has many properties, with one property as a unique identifier, usually called `number` or `id`. You can always get a single item if you already know the unique identifier. In case the unique identifier is not known, you can always search the collection and retrieve an array of items that satisfy the search criteria, or retrieve only the count of items that satisfy the search criteria. When you search for items in a collection, you can always use filtering, sorting and pagination. When it comes to pagination, we offer two distinct approaches available on separate endpoints. You can read more about filtering, sorting and pagination in the following sections. \n\n## Filtering\n\nFiltering is enabled on all collection endpoints but not on all properties.\n\nFiltering on collections can be done using the query string parameter `filter`. A filter is made up of a set of predicates and follows a syntax inspired by mongoDB. A predicate is made up of a property name, an operator, and a value.\n\nExample: `?filter=name$eq:Joe`\n\nThis matches all resources with the value Joe in the property name.\n\nPredicates can be chained using either of the logical operators AND and OR.\n\nExample: `?filter=name$eq:Joe$and:city$like:*port`\n\nFiltering on strings is case insensitive.\n\n#### Filterable properties\nInformation about what properties allow filtering and on what operators can be found in the property in the schema for the collection. Each property that allows filtering has the property `\"x-filterable\"` in combination with `operators` set. If you try to filter on something that isn’t allowed the server will respond with a status code 400.\n\n#### Specifying Operator affinity\nIf you want to control the operator affinity then you can use parentheses.\n\nAn example is: `?filter=name$eq:Joe$and:(city$like:*port$or:age$lt:40)`\n\n#### URL Encoding\nURL parameter values should always be URL compatible. Always URL encode filter strings.\n\n#### Filter Operators\nThe possible filtering operators are:\n\n| Operator | Syntax |\n| -------- | ------ |\n|Equals | $eq:|\n|Not equals | $ne:|\n|Greater than | $gt:|\n|Greater than or equal | $gte:|\n|Less than | $lt:|\n|Less than or equal | $lte:|\n|Substring match\t| $like:|\n|And also | $and:|\n|Or else | $or:|\n|In | $in:|\n|Not In | $nin:|\n\n#### Substring matching\n\nThe `$like:` operator supports both using wildcards (*) and not using wildcards. If no wildcards are used, the expression is considered a `contains` expression and effectively becomes a filter with a wildcard at the start of the string and one at the end of the string. This operator is only allowed on some properties.\n\n#### Escaping special characters in your filter\nTo not interfere with the parsing of the filter expression, certain escape sequences are necessary.\n\n- “$” is escaped with “$$”\n- “(” is escaped with “$(”\n- “)” is escaped with “$)”\n- “*” is escaped with “$*”\n- “,” is escaped with “$,”\n- “[” is escaped with “$[”\n- “]” is escaped with “$]”\n\n#### Using null values in your filter\nShould you want to filter for the nonexistence of a property (i.e. null value) you can use the null escape sequence.\n\n`$null:`\n\n#### Using in and not in operators\nTo determine whether a specified value matches any value in (or not in) a list you filter using the `$in:` or `$nin:` operator. The list to filter by has to be enclosed in brackets and values separated by commas.\n\n`customerNumber$in:[2,5,7,22,45]`\n\n\nIt is possible to also use the `$null:` keyword if you wish to include that in the filter. The max supported length of an array using the `$in:` or `$nin:` operator is 200.\n\n\n## Sorting\n\nSorting on strings is case insensitive.\n\n### Sort ascending\n\nSorting on collections can be done using the query string parameter ‘sort’.\n\n```\n?sort=name\n```\n\n### Sort descending\n\nThe default sort direction is ascending, but this can be turned by prepending a minus (-).\n\n```\n?sort=-name\n```\n\n### Sort by multiple properties\n\nIf you need to sort by multiple properties these can just be separated by commas. Mixing of directions is allowed.\n\n```\n?sort=-name,age\n```\n\n### Sort alphabetically\n\nIn certain cases, you might want to enforce that even numeric values are sorted alphabetically, so 1000 is less than 30. In those cases, you can prepend the sort property with a tilde (~).\n\n```\n?sort=~name\n```\n\n#### Sortable properties\nInformation about what properties are sortable can be found in the schema for the collection. Each property that allows sorting has the property `\"x-sortable\": true` set.\n\n\n## Pagination\n\nWhen it comes to retrieving a collection of items, you can use two distinct approaches:\n\n* **Cursor-based pagination** (continued loading of items using a `cursor` as a query parameter to get the next page of items)\n * This is the recommended approach, and the one you should use by default.\n * The endpoint naming scheme is **\"Retrieve all `Items`\"**. (Usage: `/{ITEM}?cursor={CURSOR_VALUE}`)\n * The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller.\n\n\n* **Classic pagination** (limited functionality*. Specify `skippages` and `pagesize ` as query parameters to get a specific page of items)\n * You should only consider using classic pagination, if you rely on loading pages (i.e. for list views or table/grid-based UI's).\n * The endpoint naming scheme is **\"Retrieve a page of `Items`\"**. (Usage: `/{ITEM}/paged?skippages=0&pagesize=20`) \n \\* It's important to note that there is a limit of 10.000 items using this approach. Any items outside of the first 10.000 items will not be loaded.\n\nPlease bear in mind that the two approaches are supported by **separate endpoints**. To use classic pagination, add `/paged` to your request URL.\n\nIf you need to know the total count of items that you can expect to get from your search, you can use a separate endpoint called **\"Retrieve the number of `Items`\"**. \nYou can also use the result of this endpoint to calculate the pagination navigation buttons for a table/grid-based UI.\n\n\n### Which approach should you use?\nWe highly recommend that you use cursor-based pagination because:\n- It is more performant and offers much faster retrieval of items;\n- It can be used for very large collections of many thousands or millions of items, whereas classic pagination is limited to only returning 10.000 results, everything else is ignored; \n Classic pagination is only appropriate when you have an app with a table/grid-based UI.\n\n### Cursor-based pagination\n\n#### How it works\n\nWhen you search for items in a large collection, the response will contain a first set of items and a cursor that you can use in a subsequent request to get the next series of items. This way you can retrieve the next set of items only when needed (if the first set suffices, you don't need to send a second request).\n\nThe first set of items usually consists of 1000 results. If the cursor is not present in the response, it means that there are no more items in the results. \n\nPlease note that the cursor represents the `identifier` of the first `item` on the next set and it should not be mistaken for the number of items which are yet to be displayed.\n\n##### Real world example\n\nI want to retrieve all subscriptions.\n\n1) I send a request to https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions \n and get back an array of 1.000 subscriptions, and a cursor with value 34781\n\n2) I send a request for the next items in the resulting collection:\n https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions?cursor=34781\n and get back an array of 1.000 subscriptions and a cursor with value 87695\n\n3) I send a request for the next items in the result:\n https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions?cursor=87695\n and get back an array of 56 items and no cursor.\n No cursor means I have retrieved all the subscriptions, i.e. I have reached the end of the list.\n\n### Classic pagination\n\nIf no parameters are used, the collection endpoint returns 20 items at a time. URL parameters allow you to increase this to up to 100 items or to skip pages if necessary.\n\n##### Real world example\n\nI want to show a grid with page size of 50 and pagination navigation buttons.\n\n1) I send a request to see how many subscriptions there are in the collection: \n `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/count` \n I get the number of subscriptions in the collection, `2056`, and I can calculate the number of pages to be 2056 divided by 50 = 40 with 6 as remainder, meaning I have 41 pages total. I can then use this to present the user the navigation buttons.\n\n\n2) I send a request to retrieve the first page of subscriptions that my user will see: \n `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/paged?pagesize=50&sort=-number` \n with this I get back an array of 50 subscriptions, sorted by number in descending order.\n\n\n3) Now if the user wants to see page number 6, I'll send a new request, skipping the first 5 pages to get the subscriptions from page number 6: \n `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/paged?pagesize=50&skippages=5&sort=-number` \n I get back an array of 50 subscriptions that belong to page number 6 when sorting by number in descending order.\n\n\n\n### Number of items in a collection\n\nAs mentioned before we offer endpoint to get the count of items in the collection. You can also use this info for calculation of pagination navigation in case of classic pagination.\n\nExample `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/count`\n\n\n## HTTP Status Codes\n\nThe Open API returns these HTTP status codes.\n\n| Code | Text | Description |\n| :--- | :--------------------- | :----------------------------------------------------------- |\n| 200 | OK | Everything is OK |\n| 201 | Created | When you create resources, this is what you get. This will be accompanied by the created resource in the body and a location header with a link to the created resource. |\n| 204 | No Content | In certain cases there is nothing to return. So we will let you know by returning a 204. |\n| 400 | Bad Request | The request you made was somehow malformed. A malformed request could be failed validation on creation or updating. If you try to filter on something that isn’t filterable this is also what you’ll see. Whenever possible we will also try to include a developer hint to help you get around this issue. |\n| 401 | Unauthorized | The credentials you supplied us with weren’t correct, or perhaps you forgot them altogether. If an agreement has revoked the grant they gave your app, this is what you will see. |\n| 403 | Forbidden | You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued. |\n| 404 | Not Found | This is returned when you try to request something that doesn’t exist. This could be a resource that has been deleted or just a URL you tried to hack. If you see a lot of these, it could be an indication that you aren’t using the links provided by the API. You should never need to concatenate any URLs. The API should provide you with the links needed. |\n| 405 | Method Not Allowed | Not all endpoints support all HTTP methods. If you try issue a PUT request to a collection resource this is what you get. |\n| 409 | Conflict | The request cannot be completed due to a conflict with the current state of the target resource. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update. |\n| 415 | Unsupported Media Type | Our API is a JSON api. If you ask us to give you anything else, we give you this, and tell you why in the JSON body of the response. |\n| 500 | Internal Server Error | We don’t like to see these, and they are flagged in our logs. When you see this, something went wrong on our end. Either try again, or contact our support. |\n\n\n## Required and Readonly Properties\n\nSince OpenAPI allows client generation based on the specification, we decided to use the same model/schema in our for both read and write endpoints where possible.\n\nThis led us to chose not to have the Id/Number in the URL parameter for PUT requests, but to use the one from the body, so there is no confusion.\n\nWhen a property is marked as `required` it means you need to provide a value on each POST and PUT requests.\n\nWhen a property is marked as `readonly` it means you should provide the same value you get in the GET requests, or do not send the property in the JSON at all (skip it).\n\n## Resource encoding\n\nAll non-alphanumeric characters in resource URLs are standard URL encoded. Please refer to standard URL encoding.\n\n## Implementation specifics\n\nHelpful details to know when implementing e-conomic REST.\n\n### Booleans\n\nBooleans should only be expected to be represented in responses when true. A false boolean is omitted from response body. The same logic applies to write operations such as POST and PUT.\n\n### Nulling\n\nWe do not generally accept null as a value and a validation exception should be expected. To null a property you must exclude it from your JSON on the write operation.\n\n## Object version\n\nObjectVersion is the mechanism that enforces updates only on latest state of an object.\nObjectVersion property is mandatory in Put Requests.\nObjectVersion property is retrieved on Get Request and needs to be included in Put Request.\nIf object was modified between Get and Put requests, Put request will fail with error code `409 Conflict`\n\n```\n{\n \"message\": \"Update conflict. Version does not match.\",\n \"developerHint\": \"The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.\",\n \"logId\": \"09580053-1141-4e7f-85e1-bed8600e0278\",\n \"logTimeUtc\": \"2021-11-04T09:07:56\",\n \"property\": \"version\"\n}\n```\n\n## Idempotency tokens\n Idempotency tokens are unique keys that help maintain the integrity of operations on the API’s. These tokens prevent accidental duplication of requests, ensuring that the same operation is not performed multiple times, even if the same request is sent repeatedly.\n \n When making a request, you can set `Idempotency-Key` header with your own unique value for that specific request. In case of a network failure, if you don't get the response, you can retry the request with the same value for the header. Our system will prevent duplicate requests, instead you will get the original response from our cache. \n \n Keep in mind that this is cached for only **one hour** window. \n \n When we return a response from the cache, we set a response header `X-ResultFromCache` to true.\n\n It’s important to note that **you will be responsible for generating and keeping track of these keys**.\n\n The [Idempotency Tokens feature](https://techtalk.e-conomic.com/idempotency-tokens-in-e-conomic-apis/) is not available for GET requests.\n\n## The LastUpdated property of resources\nThe `LastUpdated` property shows the date when a resource was last updated. A resource is considered updated when any property in the model has been changed. If there aren’t any changes, `LastUpdated` will equal the creation date.\n \nPlease bear in mind that not all resources have a LastUpdated property. Also, sometimes the LastUpdated property is modified by changes to a different model, as is the case with sales documents.\nFor example, when an invoice line is created, the LastUpdated field on the invoice is modified.\n \nThe main usage for this property is to help retrieve only data that has been changed within a certain interval. This will save processing and network time spent on the client side.\n \nUsage example: https://apis.e-conomic.com/productsapi/v2.0.0/the_resource?filter=lastUpdated$gt:2024-02-24\nThis will return all resources which have been updated since 24.02.2024.\n \n## Custom extensions in OpenAPI specification\n\nIn the specification file, there are some custom extensions that developers can make use of when consuming endpoints. Those extensions are always prefixed by `x-`.\n\n1) `x-required-roles`: The list of roles that are required for each group of endpoints.\n2) `x-error-codes`: The list of error codes that are potentially returned from each group of endpoints.\n3) `x-cursor-page-size`: The maximum number of items in `CursorResults` that can be returned in a single call.\n\n\n# Authentication\n\n**Create tokens as instructed [here](https://www.e-conomic.com/developer/connect) and then add these two authentication headers to your requests:**\n\n", + "contact": { + "url": "https://www.e-conomic.com/", + "email": "api@e-conomic.com" + }, + "version": "2.0.0", + "x-logo": { + "url": "", + "altText": "Visma e-conomic" + } + }, + "servers": [ + { + "url": "https://apis.e-conomic.com/productsapi/v2.0.0/" + } + ], + "paths": { + "/productgroups/{productGroupNumber}/accrualsettings": { + "get": { + "tags": [ + "AccrualSettings" + ], + "summary": "Retrieve accrual settings", + "description": "Retrieve the accrual settings for the product group.", + "operationId": "GetAccrualSettings", + "parameters": [ + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the accrual settings", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccrualSettings" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "AccrualSettings" + ], + "summary": "Update accrual settings", + "description": "Update the accrual settings for the product group.", + "operationId": "PutAccrualSettings", + "parameters": [ + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the accrual settings", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccrualSettings" + }, + "example": {} + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/{number}": { + "get": { + "tags": [ + "PriceGroups" + ], + "summary": "Retrieve single PriceGroup", + "description": "Use this endpoint to retrieve a PriceGroup.", + "operationId": "GetOnePriceGroup", + "parameters": [ + { + "name": "number", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PriceGroup" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "PriceGroups" + ], + "summary": "Delete single PriceGroup", + "description": "Use this endpoint to delete a single PriceGroup by number.", + "operationId": "DeletePriceGroupById", + "parameters": [ + { + "name": "number", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups": { + "get": { + "tags": [ + "PriceGroups" + ], + "summary": "Retrieve all PriceGroups", + "description": "Use this endpoint to retrieve all PriceGroups in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllPriceGroups", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PriceGroupCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "PriceGroups" + ], + "summary": "Create a single PriceGroup", + "description": "Use this endpoint to create a single PriceGroup.", + "operationId": "CreatePriceGroup", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PriceGroup" + }, + "example": { + "number": 0, + "name": "string" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "PriceGroups" + ], + "summary": "Update a single PriceGroup", + "description": "Use this endpoint to update a single PriceGroup.", + "operationId": "UpdatePriceGroupById", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PriceGroup" + }, + "example": { + "number": 0, + "name": "string" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/paged": { + "get": { + "tags": [ + "PriceGroups" + ], + "summary": "Retrieve a page of PriceGroups", + "description": "Use this endpoint to load a page of PriceGroups.", + "operationId": "GetPageOfPriceGroups", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PriceGroup" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/count": { + "get": { + "tags": [ + "PriceGroups" + ], + "summary": "Retrieve the number of PriceGroups", + "description": "Call this endpoint to get the number of PriceGroups. You can use filtering as well.", + "operationId": "GetNumberOfPriceGroups", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/count": { + "get": { + "tags": [ + "ProductGroups" + ], + "summary": "Retrieve the number of ProductGroups", + "description": "Call this endpoint to get the number of ProductGroups. You can use filtering as well.", + "operationId": "GetNumberOfProductGroups", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/{number}": { + "get": { + "tags": [ + "ProductGroups" + ], + "summary": "Retrieve single ProductGroup", + "description": "Use this endpoint to load a single ProductGroup by number.", + "operationId": "GetProductGroupById", + "parameters": [ + { + "name": "number", + "in": "path", + "description": "The number corresponding to the id of the product group", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductGroup" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "ProductGroups" + ], + "summary": "Delete single ProductGroup", + "description": "Use this endpoint to delete a single ProductGroup by number.", + "operationId": "DeleteProductGroupById", + "parameters": [ + { + "name": "number", + "in": "path", + "description": "The number corresponding to the id of the product group", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups": { + "get": { + "tags": [ + "ProductGroups" + ], + "summary": "Retrieve all ProductGroups", + "description": "Use this endpoint to retrieve all ProductGroups in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllProductGroups", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductGroupCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "ProductGroups" + ], + "summary": "Update a single ProductGroup", + "description": "Use this endpoint to update a single ProductGroup.", + "operationId": "UpdateProductGroupById", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductGroup" + }, + "example": { + "id": 0, + "name": "string", + "domesticAccountId": 0 + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "ProductGroups" + ], + "summary": "Create a single ProductGroup", + "description": "Use this endpoint to create a single ProductGroup.", + "operationId": "CreateProductGroup", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductGroup" + }, + "example": { + "id": 0, + "name": "string", + "domesticAccountId": 0 + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductGroupCreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/paged": { + "get": { + "tags": [ + "ProductGroups" + ], + "summary": "Retrieve a page of ProductGroups", + "description": "Use this endpoint to load a page of ProductGroups.", + "operationId": "GetPageOfProductGroups", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductGroup" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/products/{productNumber}": { + "get": { + "tags": [ + "Products" + ], + "summary": "Retrieve single Product", + "description": "Use this endpoint to retrieve a Product.", + "operationId": "GetOneProduct", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "Products" + ], + "summary": "Update a single Product", + "description": "Use this endpoint to update a single Product.", + "operationId": "UpdateProductById", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + }, + "example": { + "productNumber": "string", + "productGroupId": 0, + "name": "string", + "isAccessible": false + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Products" + ], + "summary": "Delete single Product", + "description": "Use this endpoint to delete a single Product by number.", + "operationId": "DeleteProductById", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/products": { + "get": { + "tags": [ + "Products" + ], + "summary": "Retrieve all Products", + "description": "Use this endpoint to retrieve all Products in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllProducts", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "Products" + ], + "summary": "Create a single Product", + "description": "Use this endpoint to create a single Product.", + "operationId": "CreateProduct", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + }, + "example": { + "productNumber": "string", + "productGroupId": 0, + "name": "string", + "isAccessible": false + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductCreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productspaged/paged": { + "get": { + "tags": [ + "Products" + ], + "summary": "Retrieve a page of Products", + "description": "Use this endpoint to load a page of Products.", + "operationId": "GetPageOfProducts", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/products/{productNumber}/salespriceincurrencies/{currency}": { + "get": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Retrieve single ProductSalesPriceInCurrency", + "description": "Use this endpoint to retrieve a ProductSalesPriceInCurrency.", + "operationId": "GetOneProductSalesPriceInCurrency", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "currency", + "in": "path", + "required": true, + "schema": { + "pattern": "^[A-Za-z]*$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrency" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Delete single ProductSalesPriceInCurrency", + "description": "Use this endpoint to delete a single ProductSalesPriceInCurrency by number.", + "operationId": "DeleteProductSalesPriceInCurrencyById", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "currency", + "in": "path", + "required": true, + "schema": { + "pattern": "^[A-Za-z]*$", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/salespriceincurrencies": { + "get": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Retrieve all ProductSalesPriceInCurrencies", + "description": "Use this endpoint to retrieve all ProductSalesPriceInCurrencies in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllProductSalesPriceInCurrencies", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrencyCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/salespriceincurrencies/paged": { + "get": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Retrieve a page of ProductSalesPriceInCurrencies", + "description": "Use this endpoint to load a page of ProductSalesPriceInCurrencies.", + "operationId": "GetPageOfProductSalesPriceInCurrencies", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrency" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/salespriceincurrencies/count": { + "get": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Retrieve the number of ProductSalesPriceInCurrencies", + "description": "Call this endpoint to get the number of ProductSalesPriceInCurrencies. You can use filtering as well.", + "operationId": "GetNumberOfProductSalesPriceInCurrencies", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/products/{productNumber}/salespriceincurrencies": { + "post": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Create a single ProductSalesPriceInCurrency", + "description": "Use this endpoint to create a single ProductSalesPriceInCurrency.", + "operationId": "CreateProductSalesPriceInCurrency", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrency" + }, + "example": { + "currency": "string", + "salesPrice": 0 + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrencyCreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "ProductSalesPriceInCurrencies" + ], + "summary": "Update a single ProductSalesPriceInCurrency", + "description": "Use this endpoint to update a single ProductSalesPriceInCurrency.", + "operationId": "UpdateProductSalesPriceInCurrencyById", + "parameters": [ + { + "name": "productNumber", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrency" + }, + "example": { + "currency": "string", + "salesPrice": 0 + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/{priceGroupNumber}/specialprices/{number}": { + "get": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Retrieve single ProductSpecialPrice", + "description": "Use this endpoint to retrieve a ProductSpecialPrice.", + "operationId": "GetOneProductSpecialPrice", + "parameters": [ + { + "name": "number", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPrice" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Delete single ProductSpecialPrice", + "description": "Use this endpoint to delete a single ProductSpecialPrice by number.", + "operationId": "DeleteProductSpecialPriceById", + "parameters": [ + { + "name": "number", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/{priceGroupNumber}/specialprices": { + "get": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Retrieve all ProductSpecialPrices", + "description": "Use this endpoint to retrieve all ProductSpecialPrices in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllProductSpecialPrices", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPriceCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Create a single ProductSpecialPrice", + "description": "Use this endpoint to create a single ProductSpecialPrice.", + "operationId": "CreateProductSpecialPrice", + "parameters": [ + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPrice" + }, + "example": { + "productNumber": "string" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Update a single ProductSpecialPrice", + "description": "Use this endpoint to update a single ProductSpecialPrice.", + "operationId": "UpdateProductSpecialPriceById", + "parameters": [ + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPrice" + }, + "example": { + "productNumber": "string" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/{priceGroupNumber}/specialprices/paged": { + "get": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Retrieve a page of ProductSpecialPrices", + "description": "Use this endpoint to load a page of ProductSpecialPrices.", + "operationId": "GetPageOfProductSpecialPrices", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + }, + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSpecialPrice" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/pricegroups/{priceGroupNumber}/specialprices/count": { + "get": { + "tags": [ + "ProductSpecialPrices" + ], + "summary": "Retrieve the number of ProductSpecialPrices", + "description": "Call this endpoint to get the number of ProductSpecialPrices. You can use filtering as well.", + "operationId": "GetNumberOfProductSpecialPrices", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "priceGroupNumber", + "in": "path", + "description": "The number of the price group for the product special price(s)", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/specialprices/{productSpecialPriceNumber}/specialpricesincurrency/{currency}": { + "get": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Retrieve single ProductSpecialPriceInCurrency", + "description": "Use this endpoint to retrieve a ProductSpecialPriceInCurrency.", + "operationId": "GetOneProductSpecialPriceInCurrency", + "parameters": [ + { + "name": "currency", + "in": "path", + "required": true, + "schema": { + "pattern": "^[A-Za-z]*$", + "type": "string" + } + }, + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrency" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Delete single ProductSpecialPriceInCurrency", + "description": "Use this endpoint to delete a single ProductSpecialPriceInCurrency by number.", + "operationId": "DeleteProductSpecialPriceInCurrencyById", + "parameters": [ + { + "name": "currency", + "in": "path", + "required": true, + "schema": { + "pattern": "^[A-Za-z]*$", + "type": "string" + } + }, + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/specialprices/{productSpecialPriceNumber}/specialpricesincurrency": { + "get": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Retrieve all ProductSpecialPricesInCurrency", + "description": "Use this endpoint to retrieve all ProductSpecialPricesInCurrency in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllProductSpecialPricesInCurrency", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrencyCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Create a single ProductSpecialPriceInCurrency", + "description": "Use this endpoint to create a single ProductSpecialPriceInCurrency.", + "operationId": "CreateProductSpecialPriceInCurrency", + "parameters": [ + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrency" + }, + "example": { + "currency": "string", + "salesPrice": 0 + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrencyCreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Update a single ProductSpecialPriceInCurrency", + "description": "Use this endpoint to update a single ProductSpecialPriceInCurrency.", + "operationId": "UpdateProductSpecialPriceInCurrencyById", + "parameters": [ + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrency" + }, + "example": { + "currency": "string", + "salesPrice": 0 + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/specialprices/{productSpecialPriceNumber}/specialpricesincurrency/paged": { + "get": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Retrieve a page of ProductSpecialPricesInCurrency", + "description": "Use this endpoint to load a page of ProductSpecialPricesInCurrency.", + "operationId": "GetPageOfProductSpecialPricesInCurrency", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + }, + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrency" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/specialprices/{productSpecialPriceNumber}/specialpricesincurrency/count": { + "get": { + "tags": [ + "ProductSpecialPricesInCurrency" + ], + "summary": "Retrieve the number of ProductSpecialPricesInCurrency", + "description": "Call this endpoint to get the number of ProductSpecialPricesInCurrency. You can use filtering as well.", + "operationId": "GetNumberOfProductSpecialPricesInCurrency", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "productSpecialPriceNumber", + "in": "path", + "description": "The number of the product special price for the product special price(s) in other currency", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/{productGroupNumber}/projectsettings": { + "get": { + "tags": [ + "ProjectSettings" + ], + "summary": "Retrieve project settings", + "description": "Retrieve the project settings for the product group.", + "operationId": "GetProjectSettings", + "parameters": [ + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the project settings", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectSettings" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "ProjectSettings" + ], + "summary": "Update project settings", + "description": "Update the project settings for the product group.", + "operationId": "PutProjectSettings", + "parameters": [ + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the project settings", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectSettings" + }, + "example": { + "isInProject": false + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/{productGroupNumber}/zones/count": { + "get": { + "tags": [ + "Zones" + ], + "summary": "Retrieve the number of Zones", + "description": "Call this endpoint to get the number of Zones. You can use filtering as well.", + "operationId": "GetNumberOfZones", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/{productGroupNumber}/zones/{number}": { + "get": { + "tags": [ + "Zones" + ], + "summary": "Retrieve single Zone", + "description": "Use this endpoint to load a single Zone by number.", + "operationId": "GetZoneById", + "parameters": [ + { + "name": "number", + "in": "path", + "description": "The number corresponding to the id of the zone", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Zone" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Zones" + ], + "summary": "Delete single Zone", + "description": "Use this endpoint to delete a single Zone by number.", + "operationId": "DeleteZoneById", + "parameters": [ + { + "name": "number", + "in": "path", + "description": "The number corresponding to the id of the zone", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/{productGroupNumber}/zones": { + "get": { + "tags": [ + "Zones" + ], + "summary": "Retrieve all Zones", + "description": "Use this endpoint to retrieve all Zones in bulk.
The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller than that.
Use the continuation cursor parameter to set the cursor for retrieval of the next set of data.
Please check the [pagination instructions](#section/Retrieving-data/Pagination).", + "operationId": "GetAllZones", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ZoneCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "Zones" + ], + "summary": "Create a single Zone", + "description": "Use this endpoint to create a single Zone.", + "operationId": "CreateZone", + "parameters": [ + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Zone" + }, + "example": { + "id": 0, + "accountId": 0 + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ZoneCreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "Zones" + ], + "summary": "Update a single Zone", + "description": "Use this endpoint to update a single Zone.", + "operationId": "UpdateZoneById", + "parameters": [ + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Zone" + }, + "example": { + "id": 0, + "accountId": 0 + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/productgroups/{productGroupNumber}/zones/paged": { + "get": { + "tags": [ + "Zones" + ], + "summary": "Retrieve a page of Zones", + "description": "Use this endpoint to load a page of Zones.", + "operationId": "GetPageOfZones", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + }, + { + "name": "productGroupNumber", + "in": "path", + "description": "The number identifying the product group for the zones", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Zone" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AccrualSettings": { + "type": "object", + "properties": { + "accrualAccountId": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The id of the account for accruals

Filterable: not filterable

Sortable: false

", + "format": "int32", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "CreatedResult": { + "type": "object", + "properties": { + "number": { + "type": "integer", + "description": "

Filterable: not filterable

Sortable: false

", + "format": "int32", + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "Error": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "errorCode": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "property": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "PriceGroup": { + "required": [ + "name", + "number" + ], + "type": "object", + "properties": { + "number": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The unique number of the price group

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "name": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "The name of the price group

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "PriceGroupCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PriceGroup" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + }, + "ProblemDetails": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string" + }, + "status": { + "type": "integer", + "format": "int32" + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Error" + }, + "nullable": true + }, + "traceId": { + "type": "string" + }, + "errorCode": { + "type": "string" + }, + "traceTimeUtc": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Product": { + "required": [ + "isAccessible", + "name", + "productGroupId", + "productNumber" + ], + "type": "object", + "properties": { + "productNumber": { + "maxLength": 25, + "minLength": 1, + "type": "string", + "description": "Identifier for the product, must be unique.

Filterable: eq, ne, lt, lte, gt, gte, like

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, like", + "x-sortable": true + }, + "productGroupId": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "Identifier for the group the product belongs to.

Filterable: eq, ne, lt, lte, gt, gte

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte", + "x-sortable": true + }, + "name": { + "maxLength": 300, + "minLength": 1, + "type": "string", + "description": "Name of the product. Must be non-empty.

Filterable: eq, ne, lt, lte, gt, gte, like

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, like", + "x-sortable": true + }, + "description": { + "maxLength": 500, + "type": "string", + "description": "Description of the product.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "barCode": { + "maxLength": 50, + "type": "string", + "description": "Bar code of the product.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "salesPrice": { + "type": "number", + "description": "Default sales price of the product in the base currency of the agreement.

Filterable: not filterable

Sortable: false

", + "format": "double", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "costPrice": { + "type": "number", + "description": "Default cost price of the product in the base currency of the agreement.

Filterable: not filterable

Sortable: false

", + "format": "double", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "recommendedPrice": { + "type": "number", + "description": "Default recommended price of the product in the base currency of the agreement.

Filterable: not filterable

Sortable: false

", + "format": "double", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "unitId": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "Default 1st unit of the product.

Filterable: not filterable

Sortable: false

", + "format": "int32", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "unit2Id": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "Default 2nd unit of the product.

Filterable: not filterable

Sortable: false

", + "format": "int32", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "isAccessible": { + "type": "boolean", + "description": "Whether or not the product is available for use or barred.

Filterable: eq, ne

Sortable: false

", + "x-filterable": "eq, ne", + "x-sortable": false + }, + "lastUpdated": { + "type": "string", + "description": "When the product was last updated. Will be ignored if passed when creating or updating a product, as it is set internally.

Filterable: eq, ne, lt, lte, gt, gte

Sortable: true

", + "format": "date-time", + "nullable": true, + "x-filterable": "eq, ne, lt, lte, gt, gte", + "x-sortable": true + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten by your update request.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "ProductCreatedResult": { + "type": "object", + "properties": { + "productNumber": { + "maxLength": 25, + "minLength": 1, + "type": "string", + "description": "Identifier for the product, must be unique.

Filterable: eq, ne, lt, lte, gt, gte, like

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, like", + "x-sortable": true + } + }, + "additionalProperties": false + }, + "ProductCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + }, + "ProductGroup": { + "required": [ + "domesticAccountId", + "id", + "name" + ], + "type": "object", + "properties": { + "id": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The id of the product group

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "name": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "The name of the product group

Filterable: eq, ne, lt, lte, gt, gte, in, nin, like

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin, like", + "x-sortable": true + }, + "domesticAccountId": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The id of the account for domestic sales

Filterable: not filterable

Sortable: false

", + "format": "int32", + "x-filterable": "not filterable", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "ProductGroupCreatedResult": { + "type": "object", + "properties": { + "id": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The id of the product group

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + } + }, + "additionalProperties": false + }, + "ProductGroupCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductGroup" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + }, + "ProductSalesPriceInCurrency": { + "required": [ + "currency", + "salesPrice" + ], + "type": "object", + "properties": { + "productNumber": { + "type": "string", + "description": "

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "nullable": true, + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "currency": { + "maxLength": 3, + "minLength": 0, + "type": "string", + "description": "

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "salesPrice": { + "type": "number", + "description": "

Filterable: not filterable

Sortable: false

", + "format": "double", + "x-filterable": "not filterable", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "ProductSalesPriceInCurrencyCreatedResult": { + "type": "object", + "properties": { + "currency": { + "maxLength": 3, + "minLength": 0, + "type": "string", + "description": "

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + } + }, + "additionalProperties": false + }, + "ProductSalesPriceInCurrencyCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSalesPriceInCurrency" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + }, + "ProductSpecialPrice": { + "required": [ + "productNumber" + ], + "type": "object", + "properties": { + "number": { + "type": "integer", + "description": "The unique number of the product special price, required for PUT requests. Ignored for POST requests.

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "productNumber": { + "minLength": 1, + "type": "string", + "description": "Ignored in PUT requests, the project number with which resource was created cannot be updated.

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: false

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": false + }, + "salesPriceBaseCurrency": { + "type": "number", + "description": "SalesPriceBaseCurrency and DiscountPercentage are mutually exclusive, meaning only one can have a value.

Filterable: not filterable

Sortable: false

", + "format": "double", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "discountPercentage": { + "maximum": 100, + "minimum": 0, + "type": "number", + "description": "Setting DiscountPercentage will delete all currency specific prices related to this entity. Setting to 50 will result in 50% discount of the original product price.

Filterable: not filterable

Sortable: false

", + "format": "double", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "ProductSpecialPriceCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSpecialPrice" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + }, + "ProductSpecialPriceInCurrency": { + "required": [ + "currency", + "salesPrice" + ], + "type": "object", + "properties": { + "currency": { + "maxLength": 3, + "minLength": 0, + "type": "string", + "description": "

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "salesPrice": { + "type": "number", + "description": "

Filterable: not filterable

Sortable: false

", + "format": "double", + "x-filterable": "not filterable", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "ProductSpecialPriceInCurrencyCreatedResult": { + "type": "object", + "properties": { + "currency": { + "maxLength": 3, + "minLength": 0, + "type": "string", + "description": "

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + } + }, + "additionalProperties": false + }, + "ProductSpecialPriceInCurrencyCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProductSpecialPriceInCurrency" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + }, + "ProjectSettings": { + "required": [ + "isInProject" + ], + "type": "object", + "properties": { + "isInProject": { + "type": "boolean", + "description": "Whether to include the product group in the project

Filterable: not filterable

Sortable: false

", + "x-filterable": "not filterable", + "x-sortable": false + }, + "projectAccountId": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The id of the account for the project

Filterable: not filterable

Sortable: false

", + "format": "int32", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "Zone": { + "required": [ + "accountId", + "id" + ], + "type": "object", + "properties": { + "id": { + "maximum": 2147483647, + "minimum": 1, + "pattern": "(?!(6|7)$).*", + "type": "integer", + "description": "The id of the zone

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "accountId": { + "maximum": 2147483647, + "minimum": 1, + "type": "integer", + "description": "The id of the account for the zone

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "ZoneCreatedResult": { + "type": "object", + "properties": { + "id": { + "maximum": 2147483647, + "minimum": 1, + "pattern": "(?!(6|7)$).*", + "type": "integer", + "description": "The id of the zone

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "format": "int32", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + } + }, + "additionalProperties": false + }, + "ZoneCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Zone" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + } + }, + "securitySchemes": { + "X-AppSecretToken": { + "type": "apiKey", + "description": "Application secret token needed to access the endpoints.", + "name": "X-AppSecretToken", + "in": "header" + }, + "X-AgreementGrantToken": { + "type": "apiKey", + "description": "Agreement grant token needed to access the endpoints.", + "name": "X-AgreementGrantToken", + "in": "header" + } + } + }, + "security": [ + { + "X-AppSecretToken": [], + "X-AgreementGrantToken": [] + } + ], + "tags": [ + { + "name": "AccrualSettings", + "description": "Modify the accrual settings for a product group.\n\n#### Examples of use\n- Define the account for a product group used for accruals\n\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "PriceGroups", + "description": "Price groups contain special prices (særpriser) for products. Each product can have its special price defined within given price group.\n\t\tOnce price group is selected for a customer, the special prices are used instead of the standard prices when creating invoices/orders/quotes for given customer.\n\n\n#### Examples of use cases:\n\n* Create a price group;\n* Update a price group;\n* Delete a price group;\n\n\nRelated guide for users: [e-copedia](https://www.e-conomic.dk/support/artikler/opret-saerpriser) (Danish).\n\n\n
Click to see the list of possible error codes.\n\n| Error Code | Description |\n| ---------- | ----------- |\n| PriceGroupNameIsRequired | PriceGroupNameIsRequired |\n| PriceGroupNameCanBe50CharMax | PriceGroupNameCanBe50CharMax |\n| PriceGroupIdExists | PriceGroupIdExists |\n| PriceGroupUsedByOneOrMoreCustomers | PriceGroupUsedByOneOrMoreCustomers |\n| PriceGroupInvalidPriceGroupId | PriceGroupInvalidPriceGroupId |\n| PriceGroupUsedByProductSpecialPrice | PriceGroupUsedByProductSpecialPrice |\n
\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-error-codes": [ + "PriceGroupIdExists", + "PriceGroupInvalidPriceGroupId", + "PriceGroupNameCanBe50CharMax", + "PriceGroupNameIsRequired", + "PriceGroupUsedByOneOrMoreCustomers", + "PriceGroupUsedByProductSpecialPrice" + ], + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "ProductGroups", + "description": "Create, modify, and delete product groups.\n\n#### Examples of use\n- Define the name of a product group\n- Define the account for a product group used for domestic VAT\n\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "ProductSalesPriceInCurrencies", + "description": "Create, modify, and delete product sales price in currencies.\n\n#### Examples of use\n- Define the sales price for a product in a given currency\n- Get the product sales price in currencies\n\n
Click to see the list of possible error codes.\n\n| Error Code | Description |\n| ---------- | ----------- |\n| ProductPriceInCurrencyAlreadyExists | ProductPriceInCurrencyAlreadyExists |\n| InvalidCurrency | InvalidCurrency |\n| ProductDoesNotExist | ProductDoesNotExist |\n
\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-error-codes": [ + "InvalidCurrency", + "ProductDoesNotExist", + "ProductPriceInCurrencyAlreadyExists" + ], + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "ProductSpecialPrices", + "description": "Product special prices allow you to define custom prices for products within a given price group. Once a price group is selected for a customer, the special prices are used instead of the standard prices when creating invoices/orders/quotes for the given customer.\n\n\n#### Examples of use cases:\n\n* Create a special price;\n* Update a special price;\n* Delete a special price;\n\n\nRelated guide for users: [e-copedia](https://www.e-conomic.dk/support/artikler/opret-saerpriser) (Danish).\n\n\n
Click to see the list of possible error codes.\n\n| Error Code | Description |\n| ---------- | ----------- |\n| ProductSpecialPriceCreateProductDoesNotExist | ProductSpecialPriceCreateProductDoesNotExist |\n| ProductSpecialPriceCreatePriceGroupDoesNotExist | ProductSpecialPriceCreatePriceGroupDoesNotExist |\n| ProductSpecialPriceCreateProductAlreadyHasSpecialPriceInPriceGroup | ProductSpecialPriceCreateProductAlreadyHasSpecialPriceInPriceGroup |\n| ProductSpecialPriceDeleteSpecialPriceUsedBySpecialPriceInCurrency | ProductSpecialPriceDeleteSpecialPriceUsedBySpecialPriceInCurrency |\n| ProductSpecialPriceCreateSalesPriceBaseCurrencyAndDiscountPercentageSpecified | ProductSpecialPriceCreateSalesPriceBaseCurrencyAndDiscountPercentageSpecified |\n| ProductSpecialPriceCreateDiscountPercentageOutOfRange | ProductSpecialPriceCreateDiscountPercentageOutOfRange |\n
\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-error-codes": [ + "ProductSpecialPriceCreateDiscountPercentageOutOfRange", + "ProductSpecialPriceCreatePriceGroupDoesNotExist", + "ProductSpecialPriceCreateProductAlreadyHasSpecialPriceInPriceGroup", + "ProductSpecialPriceCreateProductDoesNotExist", + "ProductSpecialPriceCreateSalesPriceBaseCurrencyAndDiscountPercentageSpecified", + "ProductSpecialPriceDeleteSpecialPriceUsedBySpecialPriceInCurrency" + ], + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "ProductSpecialPricesInCurrency", + "description": "Product special prices in currency allow you to define custom prices for a given foreign currency for products within a given price group. Once a price group is selected for a customer, the special prices in currency are used when creating an invoice in given currency.\n\n\n#### Examples of use cases:\n\n* Create a special price in currency;\n* Update a special price in currency;\n* Delete a special price in currency;\n\n\nRelated guide for users: [e-copedia](https://www.e-conomic.dk/support/artikler/opret-saerpriser) (Danish).\n\n\n
Click to see the list of possible error codes.\n\n| Error Code | Description |\n| ---------- | ----------- |\n| ProductSpecialPriceCurrencyCreateProductSpecialPriceDoesNotExist | ProductSpecialPriceCurrencyCreateProductSpecialPriceDoesNotExist |\n| ProductSpecialPriceCurrencyCreateCurrencyDoesNotExist | ProductSpecialPriceCurrencyCreateCurrencyDoesNotExist |\n| ProductSpecialPriceCurrencyCreateSpecialPriceInCurrencyAlreadyExists | ProductSpecialPriceCurrencyCreateSpecialPriceInCurrencyAlreadyExists |\n| ProductSpecialPriceCurrencyCreateDiscountPercentageSpecified | ProductSpecialPriceCurrencyCreateDiscountPercentageSpecified |\n
\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-error-codes": [ + "ProductSpecialPriceCurrencyCreateCurrencyDoesNotExist", + "ProductSpecialPriceCurrencyCreateDiscountPercentageSpecified", + "ProductSpecialPriceCurrencyCreateProductSpecialPriceDoesNotExist", + "ProductSpecialPriceCurrencyCreateSpecialPriceInCurrencyAlreadyExists" + ], + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "Products", + "description": "Create, modify, and delete products.\n
Click to see the list of possible error codes.\n\n| Error Code | Description |\n| ---------- | ----------- |\n| ProductNumberAlreadyExists | ProductNumberAlreadyExists |\n| ProductNumberChangeSettingNotEnabled | ProductNumberChangeSettingNotEnabled |\n| ProductGroupDoesNotExist | ProductGroupDoesNotExist |\n| ProductUsedOnInvoiceLines | ProductUsedOnInvoiceLines |\n| ProductUsedOnDraftInvoiceLines | ProductUsedOnDraftInvoiceLines |\n| ProductUsedInSubscriptions | ProductUsedInSubscriptions |\n
\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-error-codes": [ + "ProductGroupDoesNotExist", + "ProductNumberAlreadyExists", + "ProductNumberChangeSettingNotEnabled", + "ProductUsedInSubscriptions", + "ProductUsedOnDraftInvoiceLines", + "ProductUsedOnInvoiceLines" + ], + "x-required-roles": [ + "Sales", + "SuperUser" + ] + }, + { + "name": "ProjectSettings", + "description": "Modify the project settings for a product group.\n\n#### Examples of use\n- Define whether or not the product group is in a project\n- Define the account for a product group used for the project\n\n\n**Required application roles:** SuperUser or ProjectEmployee or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-required-roles": [ + "ProjectEmployee", + "Sales", + "SuperUser" + ] + }, + { + "name": "Zones", + "description": "Create, modify, and delete zones for a product group.\n\n#### VAT Zones\n\nThe id of a zone corresponds to the id of a VAT zone, and their existence on the product group links an account with the given zone for that group.\n\n| Zone id | Use |\n| --- | --- |\n| 1 | Domestic w/ VAT |\n| 2 | EU |\n| 3 | Abroad |\n| 4 | Domestic w/o VAT |\n| 5 | Project |\n| 6-7 | reserved |\n| 8+ | country specific VAT |\n\nDomestic w/ VAT must exist and its account is the same as the domestic account on the product group itself. Other VAT zones exist for specific countries and regions.\n\n#### Examples of use\n- Define the account for a product group used for a given VAT zone\n\n\n**Required application roles:** SuperUser or Sales [More info](https://www.e-conomic.com/developer/permissions)", + "x-required-roles": [ + "Sales", + "SuperUser" + ] + } + ], + "x-tagGroups": [ + { + "name": "Products", + "tags": [ + "Products" + ] + }, + { + "name": "Product groups", + "tags": [ + "AccrualSettings", + "ProductGroups", + "ProjectSettings", + "Zones" + ] + }, + { + "name": "Price groups", + "tags": [ + "PriceGroups", + "ProductSpecialPrices", + "ProductSpecialPricesInCurrency" + ] + }, + { + "name": "Product sales price in currencies", + "tags": [ + "ProductSalesPriceInCurrencies" + ] + } + ] +} \ No newline at end of file diff --git a/test/OpenAPI/v3.0/economic-webhooks.json b/test/OpenAPI/v3.0/economic-webhooks.json new file mode 100644 index 000000000..ae664218a --- /dev/null +++ b/test/OpenAPI/v3.0/economic-webhooks.json @@ -0,0 +1,1093 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Visma e-conomic OpenAPI Webhooks", + "description": "# Changelog\n
\nClick to see changelog.\n\n| Version | Description | Date |\n|:--------|:------------|:-----|\n|1.0.0| [`webhooks`](#tag/Webhooks) and [`eventTypes`](#tag/EventTypes) endpoints added. | 01/03/2026|\n\n
\n\n# TL;DR\n\n**Create tokens as instructed [here](https://www.e-conomic.com/developer/connect) and then add these three headers to your requests:**\n\n| Header | Value | What is this? |\n| :-------------------- | :------------------------- | :----------------------------------------------------------- |\n| X-AppSecretToken | YOUR_PRIVATE_TOKEN | This identifies your app. This is your secret token. Try using the value `demo`. |\n| X-AgreementGrantToken | YOUR_AGREEMENT_GRANT_TOKEN | This identifies the grant issued by an agreement, to allow your app to access the agreements data. Try using the value `demo`. |\n| Content-Type | application/json | We’re a JSON based API. This tells us that you agree with us on using JSON. |\n\n**Optional headers:**\n\n| Header | Value | What is this? |\n| :-------------------- | :------------------------- | :----------------------------------------------------------- |\n| Idempotency-Key | YOUR_IDEMPOTENCY_KEY | This represents your own unique idempotency key. Enables you to make use of our [Idempotency Tokens](#section/Retrieving-data/Idempotency-tokens) feature. You can't use this feature with GET requests. |\n\n### Examples\n\n#### jQuery\n\n```javascript/jQuery\n$.ajax({\n url: \"https://apis.e-conomic.com/webhooksapi/v1.0.0/the_resource\",\n dataType: \"json\",\n headers: {\n 'X-AppSecretToken': \"demo\",\n 'X-AgreementGrantToken': \"demo\",\n 'Accept': \"application/json\"\n },\n type: \"GET\"\n})\n .always(function (data) {\n $(\"#output\").text(JSON.stringify(data, null, 4));\n});\n```\n\n#### cURL\n\n```curl\ncurl -H \"X-AppSecretToken: demo\" -H \"X-AgreementGrantToken: demo\" https://apis.e-conomic.com/webhooksapi/v1.0.0/the_resource\n```\n\n\n\n# Introduction\n\nWelcome to the **Visma e-conomic OpenAPI** documentation!\n\nThe e-conomic API is a document-based JSON REST API. \n\nFor more in-depth information about e-conomic itself, please have a look at the e-copedia [http://wiki.e-conomic.dk](http://wiki.e-conomic.dk/).\n\n## Usage\n\n- **Generating a client** can easily be done using tools like [swagger-codegen](https://github.com/swagger-api/swagger-codegen) or others that work with [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification) specs.\n\n\n## Versioning\n\nAPI releases are versioned using a three-part versioning scheme: `{major version}.{minor version}.{patch version}`.\n\nWe broadly follow [Semantic Versioning](https://semver.org/) principles when versioning the API. The major version number is incremented when a breaking change occurs. \n\nThe format is:\n\n`/{resource-api}/v{major version}.{minor version}.{patch version}/{resource-name}`\n\nEach value of the above are integers and you should configure the specific version in each API call. \n\nAn example could be: `/subscriptionsapi/v1.0.0/subscriptions`\n\nTo track the changes of versions, please see our [changelog](#section/Changelog).\n\nWe reserve the right to deprecate versions at intervals since this allows for moving into a friendly environment for you faster.\n\n## Demo authentication\n\nIf you wish to try out the API before registering a developer agreement, you can do this by using the demo agreement, which mimics the authentication flow you will have to use when you create your app. All you have to do is specify HTTP header tokens `X-AgreementGrantToken: demo` and `X-AppSecretToken: demo`. Note however that you can only do GET requests with the demo agreement. If you want full access to our API's, you will need to register.\n\n# Retrieving data\n\nOur data is exposed as collections of items. Each item has many properties, with one property as a unique identifier, usually called `number` or `id`. You can always get a single item if you already know the unique identifier. In case the unique identifier is not known, you can always search the collection and retrieve an array of items that satisfy the search criteria, or retrieve only the count of items that satisfy the search criteria. When you search for items in a collection, you can always use filtering, sorting and pagination. When it comes to pagination, we offer two distinct approaches available on separate endpoints. You can read more about filtering, sorting and pagination in the following sections. \n\n## Filtering\n\nFiltering is enabled on all collection endpoints but not on all properties.\n\nFiltering on collections can be done using the query string parameter `filter`. A filter is made up of a set of predicates and follows a syntax inspired by mongoDB. A predicate is made up of a property name, an operator, and a value.\n\nExample: `?filter=name$eq:Joe`\n\nThis matches all resources with the value Joe in the property name.\n\nPredicates can be chained using either of the logical operators AND and OR.\n\nExample: `?filter=name$eq:Joe$and:city$like:*port`\n\nFiltering on strings is case insensitive.\n\n#### Filterable properties\nInformation about what properties allow filtering and on what operators can be found in the property in the schema for the collection. Each property that allows filtering has the property `\"x-filterable\"` in combination with `operators` set. If you try to filter on something that isn’t allowed the server will respond with a status code 400.\n\n#### Specifying Operator affinity\nIf you want to control the operator affinity then you can use parentheses.\n\nAn example is: `?filter=name$eq:Joe$and:(city$like:*port$or:age$lt:40)`\n\n#### URL Encoding\nURL parameter values should always be URL compatible. Always URL encode filter strings.\n\n#### Filter Operators\nThe possible filtering operators are:\n\n| Operator | Syntax |\n| -------- | ------ |\n|Equals | $eq:|\n|Not equals | $ne:|\n|Greater than | $gt:|\n|Greater than or equal | $gte:|\n|Less than | $lt:|\n|Less than or equal | $lte:|\n|Substring match\t| $like:|\n|And also | $and:|\n|Or else | $or:|\n|In | $in:|\n|Not In | $nin:|\n\n#### Substring matching\n\nThe `$like:` operator supports both using wildcards (*) and not using wildcards. If no wildcards are used, the expression is considered a `contains` expression and effectively becomes a filter with a wildcard at the start of the string and one at the end of the string. This operator is only allowed on some properties.\n\n#### Escaping special characters in your filter\nTo not interfere with the parsing of the filter expression, certain escape sequences are necessary.\n\n- “$” is escaped with “$$”\n- “(” is escaped with “$(”\n- “)” is escaped with “$)”\n- “*” is escaped with “$*”\n- “,” is escaped with “$,”\n- “[” is escaped with “$[”\n- “]” is escaped with “$]”\n\n#### Using null values in your filter\nShould you want to filter for the nonexistence of a property (i.e. null value) you can use the null escape sequence.\n\n`$null:`\n\n#### Using in and not in operators\nTo determine whether a specified value matches any value in (or not in) a list you filter using the `$in:` or `$nin:` operator. The list to filter by has to be enclosed in brackets and values separated by commas.\n\n`customerNumber$in:[2,5,7,22,45]`\n\n\nIt is possible to also use the `$null:` keyword if you wish to include that in the filter. The max supported length of an array using the `$in:` or `$nin:` operator is 200.\n\n\n## Sorting\n\nSorting on strings is case insensitive.\n\n### Sort ascending\n\nSorting on collections can be done using the query string parameter ‘sort’.\n\n```\n?sort=name\n```\n\n### Sort descending\n\nThe default sort direction is ascending, but this can be turned by prepending a minus (-).\n\n```\n?sort=-name\n```\n\n### Sort by multiple properties\n\nIf you need to sort by multiple properties these can just be separated by commas. Mixing of directions is allowed.\n\n```\n?sort=-name,age\n```\n\n### Sort alphabetically\n\nIn certain cases, you might want to enforce that even numeric values are sorted alphabetically, so 1000 is less than 30. In those cases, you can prepend the sort property with a tilde (~).\n\n```\n?sort=~name\n```\n\n#### Sortable properties\nInformation about what properties are sortable can be found in the schema for the collection. Each property that allows sorting has the property `\"x-sortable\": true` set.\n\n\n## Pagination\n\nWhen it comes to retrieving a collection of items, you can use two distinct approaches:\n\n* **Cursor-based pagination** (continued loading of items using a `cursor` as a query parameter to get the next page of items)\n * This is the recommended approach, and the one you should use by default.\n * The endpoint naming scheme is **\"Retrieve all `Items`\"**. (Usage: `/{ITEM}?cursor={CURSOR_VALUE}`)\n * The maximum number of items returned in a single call is defined in the `x-cursor-page-size` extension of the response type. Usually, the size is 1000, but in some cases, where we explicitly state so, it can be smaller.\n\n\n* **Classic pagination** (limited functionality*. Specify `skippages` and `pagesize ` as query parameters to get a specific page of items)\n * You should only consider using classic pagination, if you rely on loading pages (i.e. for list views or table/grid-based UI's).\n * The endpoint naming scheme is **\"Retrieve a page of `Items`\"**. (Usage: `/{ITEM}/paged?skippages=0&pagesize=20`) \n \\* It's important to note that there is a limit of 10.000 items using this approach. Any items outside of the first 10.000 items will not be loaded.\n\nPlease bear in mind that the two approaches are supported by **separate endpoints**. To use classic pagination, add `/paged` to your request URL.\n\nIf you need to know the total count of items that you can expect to get from your search, you can use a separate endpoint called **\"Retrieve the number of `Items`\"**. \nYou can also use the result of this endpoint to calculate the pagination navigation buttons for a table/grid-based UI.\n\n\n### Which approach should you use?\nWe highly recommend that you use cursor-based pagination because:\n- It is more performant and offers much faster retrieval of items;\n- It can be used for very large collections of many thousands or millions of items, whereas classic pagination is limited to only returning 10.000 results, everything else is ignored; \n Classic pagination is only appropriate when you have an app with a table/grid-based UI.\n\n### Cursor-based pagination\n\n#### How it works\n\nWhen you search for items in a large collection, the response will contain a first set of items and a cursor that you can use in a subsequent request to get the next series of items. This way you can retrieve the next set of items only when needed (if the first set suffices, you don't need to send a second request).\n\nThe first set of items usually consists of 1000 results. If the cursor is not present in the response, it means that there are no more items in the results. \n\nPlease note that the cursor represents the `identifier` of the first `item` on the next set and it should not be mistaken for the number of items which are yet to be displayed.\n\n##### Real world example\n\nI want to retrieve all subscriptions.\n\n1) I send a request to https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions \n and get back an array of 1.000 subscriptions, and a cursor with value 34781\n\n2) I send a request for the next items in the resulting collection:\n https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions?cursor=34781\n and get back an array of 1.000 subscriptions and a cursor with value 87695\n\n3) I send a request for the next items in the result:\n https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions?cursor=87695\n and get back an array of 56 items and no cursor.\n No cursor means I have retrieved all the subscriptions, i.e. I have reached the end of the list.\n\n### Classic pagination\n\nIf no parameters are used, the collection endpoint returns 20 items at a time. URL parameters allow you to increase this to up to 100 items or to skip pages if necessary.\n\n##### Real world example\n\nI want to show a grid with page size of 50 and pagination navigation buttons.\n\n1) I send a request to see how many subscriptions there are in the collection: \n `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/count` \n I get the number of subscriptions in the collection, `2056`, and I can calculate the number of pages to be 2056 divided by 50 = 40 with 6 as remainder, meaning I have 41 pages total. I can then use this to present the user the navigation buttons.\n\n\n2) I send a request to retrieve the first page of subscriptions that my user will see: \n `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/paged?pagesize=50&sort=-number` \n with this I get back an array of 50 subscriptions, sorted by number in descending order.\n\n\n3) Now if the user wants to see page number 6, I'll send a new request, skipping the first 5 pages to get the subscriptions from page number 6: \n `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/paged?pagesize=50&skippages=5&sort=-number` \n I get back an array of 50 subscriptions that belong to page number 6 when sorting by number in descending order.\n\n\n\n### Number of items in a collection\n\nAs mentioned before we offer endpoint to get the count of items in the collection. You can also use this info for calculation of pagination navigation in case of classic pagination.\n\nExample `https://apis.e-conomic.com/subscriptionsapi/v1.0.0/subscriptions/count`\n\n\n## HTTP Status Codes\n\nThe Open API returns these HTTP status codes.\n\n| Code | Text | Description |\n| :--- | :--------------------- | :----------------------------------------------------------- |\n| 200 | OK | Everything is OK |\n| 201 | Created | When you create resources, this is what you get. This will be accompanied by the created resource in the body and a location header with a link to the created resource. |\n| 204 | No Content | In certain cases there is nothing to return. So we will let you know by returning a 204. |\n| 400 | Bad Request | The request you made was somehow malformed. A malformed request could be failed validation on creation or updating. If you try to filter on something that isn’t filterable this is also what you’ll see. Whenever possible we will also try to include a developer hint to help you get around this issue. |\n| 401 | Unauthorized | The credentials you supplied us with weren’t correct, or perhaps you forgot them altogether. If an agreement has revoked the grant they gave your app, this is what you will see. |\n| 403 | Forbidden | You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued. |\n| 404 | Not Found | This is returned when you try to request something that doesn’t exist. This could be a resource that has been deleted or just a URL you tried to hack. If you see a lot of these, it could be an indication that you aren’t using the links provided by the API. You should never need to concatenate any URLs. The API should provide you with the links needed. |\n| 405 | Method Not Allowed | Not all endpoints support all HTTP methods. If you try issue a PUT request to a collection resource this is what you get. |\n| 409 | Conflict | The request cannot be completed due to a conflict with the current state of the target resource. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update. |\n| 415 | Unsupported Media Type | Our API is a JSON api. If you ask us to give you anything else, we give you this, and tell you why in the JSON body of the response. |\n| 500 | Internal Server Error | We don’t like to see these, and they are flagged in our logs. When you see this, something went wrong on our end. Either try again, or contact our support. |\n\n\n## Required and Readonly Properties\n\nSince OpenAPI allows client generation based on the specification, we decided to use the same model/schema in our for both read and write endpoints where possible.\n\nThis led us to chose not to have the Id/Number in the URL parameter for PUT requests, but to use the one from the body, so there is no confusion.\n\nWhen a property is marked as `required` it means you need to provide a value on each POST and PUT requests.\n\nWhen a property is marked as `readonly` it means you should provide the same value you get in the GET requests, or do not send the property in the JSON at all (skip it).\n\n## Resource encoding\n\nAll non-alphanumeric characters in resource URLs are standard URL encoded. Please refer to standard URL encoding.\n\n## Implementation specifics\n\nHelpful details to know when implementing e-conomic REST.\n\n### Booleans\n\nBooleans should only be expected to be represented in responses when true. A false boolean is omitted from response body. The same logic applies to write operations such as POST and PUT.\n\n### Nulling\n\nWe do not generally accept null as a value and a validation exception should be expected. To null a property you must exclude it from your JSON on the write operation.\n\n## Object version\n\nObjectVersion is the mechanism that enforces updates only on latest state of an object.\nObjectVersion property is mandatory in Put Requests.\nObjectVersion property is retrieved on Get Request and needs to be included in Put Request.\nIf object was modified between Get and Put requests, Put request will fail with error code `409 Conflict`\n\n```\n{\n \"message\": \"Update conflict. Version does not match.\",\n \"developerHint\": \"The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.\",\n \"logId\": \"09580053-1141-4e7f-85e1-bed8600e0278\",\n \"logTimeUtc\": \"2021-11-04T09:07:56\",\n \"property\": \"version\"\n}\n```\n\n## Idempotency tokens\n Idempotency tokens are unique keys that help maintain the integrity of operations on the API’s. These tokens prevent accidental duplication of requests, ensuring that the same operation is not performed multiple times, even if the same request is sent repeatedly.\n \n When making a request, you can set `Idempotency-Key` header with your own unique value for that specific request. In case of a network failure, if you don't get the response, you can retry the request with the same value for the header. Our system will prevent duplicate requests, instead you will get the original response from our cache. \n \n Keep in mind that this is cached for only **one hour** window. \n \n When we return a response from the cache, we set a response header `X-ResultFromCache` to true.\n\n It’s important to note that **you will be responsible for generating and keeping track of these keys**.\n\n The [Idempotency Tokens feature](https://techtalk.e-conomic.com/idempotency-tokens-in-e-conomic-apis/) is not available for GET requests.\n\n## The LastUpdated property of resources\nThe `LastUpdated` property shows the date when a resource was last updated. A resource is considered updated when any property in the model has been changed. If there aren’t any changes, `LastUpdated` will equal the creation date.\n \nPlease bear in mind that not all resources have a LastUpdated property. Also, sometimes the LastUpdated property is modified by changes to a different model, as is the case with sales documents.\nFor example, when an invoice line is created, the LastUpdated field on the invoice is modified.\n \nThe main usage for this property is to help retrieve only data that has been changed within a certain interval. This will save processing and network time spent on the client side.\n \nUsage example: https://apis.e-conomic.com/webhooksapi/v1.0.0/the_resource?filter=lastUpdated$gt:2024-02-24\nThis will return all resources which have been updated since 24.02.2024.\n \n## Custom extensions in OpenAPI specification\n\nIn the specification file, there are some custom extensions that developers can make use of when consuming endpoints. Those extensions are always prefixed by `x-`.\n\n1) `x-required-roles`: The list of roles that are required for each group of endpoints.\n2) `x-error-codes`: The list of error codes that are potentially returned from each group of endpoints.\n3) `x-cursor-page-size`: The maximum number of items in `CursorResults` that can be returned in a single call.\n\n\n# Authentication\n\n**Create tokens as instructed [here](https://www.e-conomic.com/developer/connect) and then add these two authentication headers to your requests:**\n\n", + "contact": { + "url": "https://www.e-conomic.com/", + "email": "api@e-conomic.com" + }, + "version": "1.0.0", + "x-logo": { + "url": "", + "altText": "Visma e-conomic" + } + }, + "servers": [ + { + "url": "https://apis.e-conomic.com/webhooksapi/v1.0.0/" + } + ], + "paths": { + "/EventTypes": { + "get": { + "tags": [ + "EventTypes" + ], + "summary": "Retrieve all event types", + "description": "Use this endpoint to retrieve all event types to be used for a webhook", + "operationId": "GetAllEventTypes", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventType" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/webhooks/{eventType}": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Retrieve a single Webhook", + "description": "Use this endpoint to retrieve a single webhook by EventType.", + "operationId": "GetWebhook", + "parameters": [ + { + "name": "eventType", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Webhook" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Webhooks" + ], + "summary": "Delete a single Webhook", + "description": "Use this endpoint to delete a single Webhook by EventType.", + "operationId": "DeleteWebhook", + "parameters": [ + { + "name": "eventType", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/webhooks": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Retrieve all Webhooks", + "description": "Use this endpoint to retrieve all webhooks defined for a customer.", + "operationId": "GetAllWebhooks", + "parameters": [ + { + "name": "cursor", + "in": "query", + "schema": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "Use this parameter to set the continuation cursor for paging. [Pagination instructions](#section/Retrieving-data/Pagination)" + } + }, + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookCursorResults" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "post": { + "tags": [ + "Webhooks" + ], + "summary": "Create a single Webhook", + "description": "Use this endpoint to create a Webhook triggered by EventTypeId. Please note that only one Webhook for the specified EventTypeId is allowed per agreement.", + "operationId": "CreateWebhook", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Webhook" + }, + "example": { + "eventType": "string", + "name": "string", + "url": "string", + "contentType": "string" + } + } + } + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookCreatedResult" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + }, + "put": { + "tags": [ + "Webhooks" + ], + "summary": "Update a single Webhook", + "description": "Use this endpoint to update a single Webhook by EventType.", + "operationId": "UpdateWebhook", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Webhook" + }, + "example": { + "eventType": "string", + "name": "string", + "url": "string", + "contentType": "string" + } + } + } + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "404": { + "description": "**Resource not found.** The resource you have been looking for does not exist.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "409": { + "description": "**Update conflict. Object version does not match.** The resource has been updated by another user. Retrieve the resource/object and try the update again. This is needed in order to prevent you from rolling back another user's update.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/webhooks/paged": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Retrieve paged webhooks", + "description": "Use this endpoint to retrieve paged webhooks.", + "operationId": "GetWebhooksPaged", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + }, + { + "name": "sort", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the sort fields and direction. [Sort instructions](#section/Retrieving-data/Sorting)" + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Use this parameter to set the page size. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 20 + } + }, + { + "name": "skipPages", + "in": "query", + "schema": { + "maximum": 100, + "minimum": 0, + "type": "integer", + "description": "Use this parameter to set number of pages to skip. [Pagination instructions](#section/Retrieving-data/Pagination)", + "format": "int32", + "default": 0 + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Webhook" + } + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + }, + "/webhooks/count": { + "get": { + "tags": [ + "Webhooks" + ], + "summary": "Retrieve the number of Webhooks", + "description": "Call this endpoint to get the number of Webhooks. You can use filtering as well.", + "operationId": "GetNumberOfWebhooks", + "parameters": [ + { + "name": "filter", + "in": "query", + "schema": { + "type": "string", + "description": "Use this parameter to set the filtering for fields. [Filtering instructions](#section/Retrieving-data/Filtering)" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "**Bad request.** Your request does not pass our validation. Check the errors array for more details.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "401": { + "description": "**Unauthorized access.** Please take a look at https://apis.e-conomic.com and follow the links to help on authorization, or use the word demo for both tokens.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "403": { + "description": "**Access forbidden.** You won’t necessarily have access to everything. So even though you were authorized we might still deny access to certain resources. This depends on the roles asked for when the grant was issued.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "429": { + "description": "**Too many requests.** You have made too many calls towards our API. You are over your quota. Need to wait a bit. Check info in the response headers.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + }, + "500": { + "description": "**Internal server error.** Something went wrong but the error has been logged. If you continue to see errors here, please contact api@e-conomic.com. Remember to include log id and agreement number when contacting support.", + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetails" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Error": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "errorCode": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "property": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "EventType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The unique event type name

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "description": { + "type": "string", + "description": "A user-friendly description of the event type.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "mergeFields": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventTypeMergeField" + }, + "description": "The merge fields available for use in webhook payload templates for this event type.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "EventTypeMergeField": { + "type": "object", + "properties": { + "fieldName": { + "type": "string", + "description": "The merge field name.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "description": { + "type": "string", + "description": "A user-friendly description of the merge field.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "A merge field available for use in webhook payload." + }, + "ProblemDetails": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string" + }, + "status": { + "type": "integer", + "format": "int32" + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Error" + }, + "nullable": true + }, + "traceId": { + "type": "string" + }, + "errorCode": { + "type": "string" + }, + "traceTimeUtc": { + "type": "string" + } + }, + "additionalProperties": false + }, + "StringStringKeyValuePair": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "value": { + "type": "string", + "description": "

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "Webhook": { + "required": [ + "contentType", + "eventType", + "name", + "url" + ], + "type": "object", + "properties": { + "eventType": { + "minLength": 1, + "type": "string", + "description": "The unique event type name triggering the webhook (INVOICE_BOOKED, CUSTOMER_UPDATED, etc). Note: only one webhook per event type per agreement is allowed.

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + }, + "name": { + "maxLength": 100, + "minLength": 0, + "type": "string", + "description": "The webhook name. This field is intended for e-conomic UI display only and has no functional impact on webhook execution.

Filterable: eq, ne, lt, lte, gt, gte, like

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, like", + "x-sortable": true + }, + "url": { + "maxLength": 1000, + "minLength": 0, + "type": "string", + "description": "The webhook URL to be called when the event is triggered.

Filterable: eq, ne, lt, lte, gt, gte, like

Sortable: false

", + "x-filterable": "eq, ne, lt, lte, gt, gte, like", + "x-sortable": false + }, + "contentType": { + "maxLength": 50, + "minLength": 0, + "type": "string", + "description": "The Content-Type header of the request sent to the Url.

Filterable: not filterable

Sortable: false

", + "x-filterable": "not filterable", + "x-sortable": false + }, + "postData": { + "maxLength": 4000, + "minLength": 0, + "type": "string", + "description": "The payload of the request sent to the Url.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "customHeaders": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StringStringKeyValuePair" + }, + "description": "A maximum of 5 custom headers to be sent to the Url. List of key-value pairs, e.g. [{\"key\": \"X-Custom-Header\", \"value\": \"value\"}].

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "isDisabled": { + "type": "boolean", + "description": "Indicates if the webhook is disabled.

Filterable: eq, ne, lt, lte, gt, gte

Sortable: false

", + "x-filterable": "eq, ne, lt, lte, gt, gte", + "x-sortable": false + }, + "lastDisabledDate": { + "type": "string", + "description": "The date and time when the webhook was last disabled.

Read-only: true

Filterable: eq, ne, lt, lte, gt, gte

Sortable: false

", + "format": "date-time", + "nullable": true, + "readOnly": true, + "x-filterable": "eq, ne, lt, lte, gt, gte", + "x-sortable": false + }, + "lastUpdated": { + "type": "string", + "description": "The date when the webhook was last updated.

Read-only: true

Filterable: eq, ne, lt, lte, gt, gte

Sortable: false

", + "format": "date-time", + "nullable": true, + "readOnly": true, + "x-filterable": "eq, ne, lt, lte, gt, gte", + "x-sortable": false + }, + "objectVersion": { + "type": "string", + "description": "The object version, required for PUT requests to help ensure that updates made by others don’t get overwritten by your update request.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false + }, + "WebhookCreatedResult": { + "type": "object", + "properties": { + "eventType": { + "minLength": 1, + "type": "string", + "description": "The unique event type name triggering the webhook (INVOICE_BOOKED, CUSTOMER_UPDATED, etc). Note: only one webhook per event type per agreement is allowed.

Filterable: eq, ne, lt, lte, gt, gte, in, nin

Sortable: true

", + "x-filterable": "eq, ne, lt, lte, gt, gte, in, nin", + "x-sortable": true + } + }, + "additionalProperties": false + }, + "WebhookCursorResults": { + "type": "object", + "properties": { + "cursor": { + "type": "string", + "description": "Use this continuation cursor in a request back to continue the list. In case there are no more items to retrieve, the cursor is not returned at all.

Read-only: true

Filterable: not filterable

Sortable: false

", + "nullable": true, + "readOnly": true, + "x-filterable": "not filterable", + "x-sortable": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Webhook" + }, + "description": "Max number of items returned is 1000.

Filterable: not filterable

Sortable: false

", + "nullable": true, + "x-filterable": "not filterable", + "x-sortable": false + } + }, + "additionalProperties": false, + "description": "Array of objects along with a cursor.", + "example": { + "cursor": "234", + "items": [ + { + "id": "13", + "name": "Patrick" + }, + { + "id": "16", + "name": "John" + } + ] + }, + "x-cursor-page-size": 1000 + } + }, + "securitySchemes": { + "X-AppSecretToken": { + "type": "apiKey", + "description": "Application secret token needed to access the endpoints.", + "name": "X-AppSecretToken", + "in": "header" + }, + "X-AgreementGrantToken": { + "type": "apiKey", + "description": "Agreement grant token needed to access the endpoints.", + "name": "X-AgreementGrantToken", + "in": "header" + } + } + }, + "security": [ + { + "X-AppSecretToken": [], + "X-AgreementGrantToken": [] + } + ], + "tags": [ + { + "name": "EventTypes", + "description": "Event types represent the various events that can trigger webhooks in the system. Use this endpoint to retrieve the list of available event types for webhook subscriptions." + }, + { + "name": "Webhooks", + "description": "Webhooks represent a type of notification that you can use in e-conomic. With a webhook, we automatically notify you when certain things happen in e-conomic, such as when an invoice is posted or a customer is updated.\n
Click to see the list of possible error codes.\n\n| Error Code | Description |\n| ---------- | ----------- |\n| WebhookInvalidUrl | The URL has invalid format (check RFC 3986). |\n| WebhookInvalidType | An unknown/unsupported webhook event type. |\n| WebhookInvalidUrlScheme | Only HTTPS is allowed for the webhook URL. |\n| WebhookReadOnly | The webhook is read-only and cannot be changed. |\n| WebhookAlreadyExistsForEventType | A webhook for this event type already exists for the agreement. Only one webhook per event type is allowed. Use PUT to update the existing webhook or DELETE to remove it before creating a new one. |\n| WebhookCustomHeadersTooLong | Custom headers exceed the maximum allowed length (1000 characters when serialized as JSON). Reduce the number or size of custom headers. |\n| WebhookCustomHeaderInvalid | One or more custom headers contain invalid characters (e.g. CRLF or non-printable). Fix or remove the header names and values. |\n| WebhookCustomHeadersTooMany | At most 5 custom headers are allowed. |\n
\n\n**Required application roles:** SuperUser [More info](https://www.e-conomic.com/developer/permissions)", + "x-error-codes": [ + "WebhookAlreadyExistsForEventType", + "WebhookCustomHeaderInvalid", + "WebhookCustomHeadersTooLong", + "WebhookCustomHeadersTooMany", + "WebhookInvalidType", + "WebhookInvalidUrl", + "WebhookInvalidUrlScheme", + "WebhookReadOnly" + ], + "x-required-roles": [ + "SuperUser" + ] + } + ], + "x-tagGroups": [ + { + "name": "EventTypes", + "tags": [ + "EventTypes" + ] + }, + { + "name": "Webhooks", + "tags": [ + "Webhooks" + ] + } + ] +} \ No newline at end of file diff --git a/test/multiple-sources.refitter b/test/multiple-sources.refitter index d013fc23d..898458d1b 100644 --- a/test/multiple-sources.refitter +++ b/test/multiple-sources.refitter @@ -2,7 +2,9 @@ "openApiPaths": [ "OpenAPI/v3.0/weather.json", "OpenAPI/v3.0/petstore.json", - "OpenAPI/v3.0/api-with-examples.json" + "OpenAPI/v3.0/api-with-examples.json", + "OpenAPI/v3.0/economic-products.json", + "OpenAPI/v3.0/economic-webhooks.json" ], "namespace": "MultipleSources", "outputFolder": "GeneratedCode",