From ed4bf72210a743d4e07188a81b830e0289b0a9d0 Mon Sep 17 00:00:00 2001 From: javorosas Date: Thu, 16 Apr 2026 15:23:09 +0200 Subject: [PATCH] Add updateDefaultSeries release and publish automation --- .github/workflows/publish.yml | 86 ++++++++++++++++++- CHANGELOG.md | 15 ++++ README.es.md | 4 +- README.md | 4 +- pom.xml | 2 +- .../resources/OrganizationsResource.java | 12 +++ .../io/facturapi/FacturapiResourcesTest.java | 28 ++++++ 7 files changed, 145 insertions(+), 6 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ce85f08..95dca56 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,7 +6,7 @@ on: - main permissions: - contents: read + contents: write jobs: publish: @@ -19,6 +19,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup Java uses: actions/setup-java@v4 @@ -87,17 +89,99 @@ jobs: return False return left_pre > right_pre + def is_greater_or_equal(left: str, right: str) -> bool: + left_parts = parse_semver(left) + right_parts = parse_semver(right) + if left_parts[:3] != right_parts[:3]: + return left_parts[:3] > right_parts[:3] + left_pre = left_parts[3] + right_pre = right_parts[3] + if left_pre == right_pre: + return True + if not left_pre: + return True + if not right_pre: + return False + return left_pre >= right_pre + publish = is_greater(version, latest) + release = is_greater_or_equal(version, latest) print(f"current_version={version}") + print(f"release_tag=v{version}") print(f"latest_version={latest}") print(f"publish={'true' if publish else 'false'}") + print(f"release={'true' if release else 'false'}") print(f"reason={'current version is newer than published version' if publish else 'published version is current or newer'}") PY + - name: Extract release notes + if: steps.gate.outputs.release == 'true' + id: notes + env: + RELEASE_VERSION: ${{ steps.gate.outputs.current_version }} + shell: bash + run: | + set -euo pipefail + + python3 <<'PY' + import os + from pathlib import Path + + version = os.environ["RELEASE_VERSION"] + changelog = Path("CHANGELOG.md") + if not changelog.exists(): + raise SystemExit("CHANGELOG.md is required to publish a release.") + + section_header = f"## [{version}]" + lines = changelog.read_text(encoding="utf-8").splitlines() + section = [] + in_section = False + + for line in lines: + if line.startswith("## ["): + if in_section: + break + if line.startswith(section_header): + in_section = True + if in_section: + section.append(line) + + if not section: + raise SystemExit(f"Could not find changelog entry for version {version}.") + + notes_path = Path("release-notes.md") + notes_path.write_text("\n".join(section).strip() + "\n", encoding="utf-8") + + output_path = Path(os.environ["GITHUB_OUTPUT"]) + with output_path.open("a", encoding="utf-8") as output: + output.write(f"notes_file={notes_path}\n") + PY + - name: Publish if: steps.gate.outputs.publish == 'true' run: mvn -B -ntp -DskipTests -DpublishRelease=true deploy + - name: Create or update GitHub Release + if: steps.gate.outputs.release == 'true' + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.gate.outputs.release_tag }} + RELEASE_NOTES_FILE: ${{ steps.notes.outputs.notes_file }} + shell: bash + run: | + set -euo pipefail + + if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + gh release edit "$RELEASE_TAG" \ + --title "$RELEASE_TAG" \ + --notes-file "$RELEASE_NOTES_FILE" + else + gh release create "$RELEASE_TAG" \ + --target "$GITHUB_SHA" \ + --title "$RELEASE_TAG" \ + --notes-file "$RELEASE_NOTES_FILE" + fi + - name: Skip publish if: steps.gate.outputs.publish != 'true' run: | diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a1b0b66 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.1.0] - 2026-04-16 +### Added +- Add `organizations.updateDefaultSeries` to set an organization's default series by document type. + +## [1.0.0] - 2026-04-07 +### Added +- Initial public release of the Java SDK. +- Ship feature parity with the rest of the official Facturapi SDKs across core resources, catalogs, uploads, downloads, and utility helpers. diff --git a/README.es.md b/README.es.md index 6b7456e..0e1dc20 100644 --- a/README.es.md +++ b/README.es.md @@ -29,14 +29,14 @@ Maven: io.facturapi facturapi-java - 1.0.0 + 1.1.0 ``` Gradle: ```gradle -implementation("io.facturapi:facturapi-java:1.0.0") +implementation("io.facturapi:facturapi-java:1.1.0") ``` ## Inicio rĂ¡pido diff --git a/README.md b/README.md index 74e188d..fe0f67b 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,14 @@ Maven: io.facturapi facturapi-java - 1.0.0 + 1.1.0 ``` Gradle: ```gradle -implementation("io.facturapi:facturapi-java:1.0.0") +implementation("io.facturapi:facturapi-java:1.1.0") ``` ## Quickstart diff --git a/pom.xml b/pom.xml index bdaf13e..3b871a5 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.facturapi facturapi-java - 1.0.0 + 1.1.0 facturapi-java Official Java SDK for Facturapi https://github.com/facturapi/facturapi-java diff --git a/src/main/java/io/facturapi/resources/OrganizationsResource.java b/src/main/java/io/facturapi/resources/OrganizationsResource.java index b2011d4..c5fb0de 100644 --- a/src/main/java/io/facturapi/resources/OrganizationsResource.java +++ b/src/main/java/io/facturapi/resources/OrganizationsResource.java @@ -201,6 +201,18 @@ public Series updateSeriesGroup(String organizationId, String seriesName, MapAPI reference + */ + public Organization updateDefaultSeries(String organizationId, Map data) { + return put("/organizations/" + organizationId + "/series-group/default-series", data, null, Organization.class); + } + /** * Deletes an invoice series. */ diff --git a/src/test/java/io/facturapi/FacturapiResourcesTest.java b/src/test/java/io/facturapi/FacturapiResourcesTest.java index e4bbbec..e7705df 100644 --- a/src/test/java/io/facturapi/FacturapiResourcesTest.java +++ b/src/test/java/io/facturapi/FacturapiResourcesTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.facturapi.enums.CancellationStatus; import io.facturapi.enums.InvoiceStatus; @@ -143,6 +144,33 @@ void organizationListLiveApiKeysIsTyped() { assertEquals("/v2/organizations/org_1/apikeys/live", request.uri().getPath()); } + @Test + void organizationDefaultSeriesCanBeUpdated() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"org_1\"}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var response = sdk.organizations().updateDefaultSeries( + "org_1", + Map.of( + "type", "I", + "series", "A" + ) + ); + + assertEquals("org_1", response.getId()); + var request = httpClient.requests().get(0); + assertEquals("PUT", request.method()); + assertEquals("/v2/organizations/org_1/series-group/default-series", request.uri().getPath()); + assertTrue(request.bodyUtf8().contains("\"type\":\"I\"")); + assertTrue(request.bodyUtf8().contains("\"series\":\"A\"")); + } + @Test void invoiceComplementsAreTypedForPagoNominaAndCustom() { StubHttpClient httpClient = new StubHttpClient();