diff --git a/Agents.md b/Agents.md
index 5b3175fd..72e7adab 100644
--- a/Agents.md
+++ b/Agents.md
@@ -2,6 +2,13 @@
This is repository with swagger generated SDK for Aspose.Barcode.Cloud service and code generating scripts. All sdk are submodules of this repo and located in `submodules` directory. Swagger specification of service API is located in `spec/aspose-barcode-cloud.json` file. Custom mustashe templates are located in `codegen/Templates` dir. Original templates for all languages are in github. Sripts for generating SDK `codegen` directory. Some post processing are in Makefiles in all submodules repo.
+## SDK repository branches
+
+Each SDK submodule is a standalone GitHub repo with its own default branch — `main` for all SDKs
+**except `go`, whose default is `v4`**. Release work ships on a `release-` branch as a draft
+pull request into that default branch. See [`doc/branches.md`](doc/branches.md) for the full per-SDK
+repository/branch table and details.
+
## Common requirements for making changes in SDK code
0. Don't commit or push in repo by youreself. But you can pull and stage in git.
@@ -14,3 +21,14 @@ This is repository with swagger generated SDK for Aspose.Barcode.Cloud service a
4.3 After generating script are end it work. Enshure there is no unstaged changes in sdk submodule.
4.4 Fix templates if generated code are not the same as you new code.
5. After templates fixed, you can end your task.
+
+## Scripting requirements
+
+- Never use Perl — neither standalone Perl scripts nor inline one-liners (e.g. `perl -pe` in shell scripts or Makefiles). Use Python instead.
+
+## Test coverage requirements
+
+Every generated SDK must satisfy two coverage bars — **API endpoint coverage ≥ 80%** and **line code
+coverage ≥ 80%** (the latter enforced by a per-SDK CI gate). `submodules/android` is exempt (it is a demo
+app, not an SDK). Regenerating or changing an SDK must not drop either metric below 80%. See
+[`doc/test-coverage-requirements.md`](doc/test-coverage-requirements.md) for the full requirements.
diff --git a/codegen/Templates/android/MainActivity.kt.mustache b/codegen/Templates/android/MainActivity.kt.mustache
index 01dacd22..39ae4597 100644
--- a/codegen/Templates/android/MainActivity.kt.mustache
+++ b/codegen/Templates/android/MainActivity.kt.mustache
@@ -30,21 +30,37 @@ import com.aspose.barcode.cloud.api.GenerateApi
import com.aspose.barcode.cloud.api.ScanApi
import com.aspose.barcode.cloud.model.BarcodeImageFormat
import com.aspose.barcode.cloud.model.BarcodeImageParams
+import com.aspose.barcode.cloud.model.Code128EncodeMode
+import com.aspose.barcode.cloud.model.Code128Params
+import com.aspose.barcode.cloud.model.ECIEncodings
import com.aspose.barcode.cloud.model.EncodeBarcodeType
+import com.aspose.barcode.cloud.model.MicroQRVersion
+import com.aspose.barcode.cloud.model.Pdf417EncodeMode
+import com.aspose.barcode.cloud.model.Pdf417ErrorLevel
+import com.aspose.barcode.cloud.model.Pdf417Params
import com.aspose.barcode.cloud.model.QREncodeMode
import com.aspose.barcode.cloud.model.QRErrorLevel
import com.aspose.barcode.cloud.model.QRVersion
import com.aspose.barcode.cloud.model.QrParams
+import com.aspose.barcode.cloud.model.RectMicroQRVersion
import com.aspose.barcode.cloud.requests.GenerateRequestWrapper
import com.aspose.barcode.cloud.requests.ScanMultipartRequestWrapper
import com.google.android.material.snackbar.Snackbar
import java.io.File
import java.io.FileOutputStream
import kotlin.math.floor
+import kotlin.math.min
import androidx.core.graphics.scale
class MainActivity : AppCompatActivity() {
companion object {
+ /** Barcode types whose modules are square and get distorted by a non-square image. */
+ private val SQUARE_BARCODE_TYPES = setOf(
+ EncodeBarcodeType.QR,
+ EncodeBarcodeType.GS1_QR,
+ EncodeBarcodeType.MICRO_QR,
+ )
+
private fun imageSize(width: Int, height: Int, maxSize: Int = 384): Size {
val ratio = width.toFloat() / height
if (ratio > 1) {
@@ -170,7 +186,7 @@ class MainActivity : AppCompatActivity() {
val smallerBmp = reduceBitmapSize(image)
barcodeImgView.setImageBitmap(smallerBmp)
- startRecognizeAnimation()
+ startProgressAnimation()
val tmpFile = File.createTempFile("barcode", null)
@@ -185,7 +201,7 @@ class MainActivity : AppCompatActivity() {
val recognized = scanApi.scanMultipart(apiRequest)
runOnUiThread {
- stopRecognizeAnimation()
+ stopProgressAnimation()
if (recognized.barcodes.isEmpty()) {
barcodeTextEdit.setText("")
showErrorMessage("No barcode detected")
@@ -198,7 +214,7 @@ class MainActivity : AppCompatActivity() {
}
} catch (e: ApiException) {
runOnUiThread {
- stopRecognizeAnimation()
+ stopProgressAnimation()
var message = e.message + ": " + e.details
if (e.httpCode == 0) {
@@ -208,7 +224,7 @@ class MainActivity : AppCompatActivity() {
}
} catch (e: Exception) {
runOnUiThread {
- stopRecognizeAnimation()
+ stopProgressAnimation()
showErrorMessage("Exception: " + e.message)
}
}
@@ -218,13 +234,13 @@ class MainActivity : AppCompatActivity() {
}
}
- private fun startRecognizeAnimation() {
+ private fun startProgressAnimation() {
val rotation = AnimationUtils.loadAnimation(this, R.anim.rotate)
rotation.fillAfter = true
barcodeImgView.startAnimation(rotation)
}
- private fun stopRecognizeAnimation() {
+ private fun stopProgressAnimation() {
barcodeImgView.clearAnimation()
}
@@ -232,30 +248,24 @@ class MainActivity : AppCompatActivity() {
val type = EncodeBarcodeType.fromValue(barcodeTypeSpinner.selectedItem.toString())
val genRequest = GenerateRequestWrapper(type, barcodeTextEdit.text.toString())
- genRequest.barcodeImageParams = BarcodeImageParams().apply {
- imageFormat = BarcodeImageFormat.PNG
- imageHeight = barcodeImgView.measuredHeight.toFloat()
- imageWidth = barcodeImgView.measuredWidth.toFloat()
- }
+ genRequest.barcodeImageParams = imageParams(type)
- if (type == EncodeBarcodeType.QR) {
- genRequest.qrParams = QrParams().apply {
- qrEncodeMode = QREncodeMode.AUTO
- qrErrorLevel = QRErrorLevel.LEVEL_M
- qrVersion = QRVersion.AUTO
- qrAspectRatio = 0.75f
- }
- }
+ applyEncodeParams(genRequest, type)
+
+ startProgressAnimation()
Thread {
try {
val generated = generateApi.generate(genRequest)
runOnUiThread {
+ stopProgressAnimation()
val bitmap = BitmapFactory.decodeFile(generated!!.absolutePath)
barcodeImgView.setImageBitmap(bitmap)
}
} catch (e: ApiException) {
runOnUiThread {
+ stopProgressAnimation()
+
var message = e.message + ": " + e.details
if (e.httpCode == 0) {
message = "Check ClientId and ClientSecret in ApiClient $message"
@@ -264,12 +274,85 @@ class MainActivity : AppCompatActivity() {
}
} catch (e: Exception) {
runOnUiThread {
+ stopProgressAnimation()
showErrorMessage("Exception: " + e.message)
}
}
}.start()
}
+ /**
+ * The API stretches the barcode to fill the requested image, so a square symbology
+ * has to be rendered into a square image to keep its modules square.
+ */
+ private fun imageParams(type: EncodeBarcodeType) = BarcodeImageParams().apply {
+ imageFormat = BarcodeImageFormat.PNG
+
+ if (type in SQUARE_BARCODE_TYPES) {
+ val side = min(barcodeImgView.measuredWidth, barcodeImgView.measuredHeight).toFloat()
+ imageHeight = side
+ imageWidth = side
+ } else {
+ imageHeight = barcodeImgView.measuredHeight.toFloat()
+ imageWidth = barcodeImgView.measuredWidth.toFloat()
+ }
+ }
+
+ /**
+ * Attaches the optional parameter groups supported by [GenerateRequestWrapper].
+ * Each group applies to its own family of barcode types.
+ */
+ private fun applyEncodeParams(request: GenerateRequestWrapper, type: EncodeBarcodeType) {
+ when (type) {
+ EncodeBarcodeType.QR,
+ EncodeBarcodeType.GS1_QR -> request.qrParams = qrParams()
+
+ EncodeBarcodeType.MICRO_QR -> request.qrParams = qrParams().apply {
+ microQRVersion = MicroQRVersion.AUTO
+ }
+
+ EncodeBarcodeType.RECT_MICRO_QR -> request.qrParams = qrParams().apply {
+ rectMicroQrVersion = RectMicroQRVersion.AUTO
+ }
+
+ EncodeBarcodeType.CODE128,
+ EncodeBarcodeType.GS1_CODE128 -> request.code128Params = Code128Params().apply {
+ code128EncodeMode = Code128EncodeMode.AUTO
+ }
+
+ EncodeBarcodeType.PDF417,
+ EncodeBarcodeType.MACRO_PDF417 -> request.pdf417Params = pdf417Params(3.0f)
+
+ EncodeBarcodeType.MICRO_PDF417,
+ EncodeBarcodeType.GS1_MICRO_PDF417 -> request.pdf417Params = pdf417Params(2.0f)
+
+ else -> Unit
+ }
+ }
+
+ private fun qrParams() = QrParams().apply {
+ qrEncodeMode = QREncodeMode.AUTO
+ qrErrorLevel = QRErrorLevel.LEVEL_M
+ qrVersion = QRVersion.AUTO
+ qrECIEncoding = ECIEncodings.UTF8
+ // 1.0 keeps the modules square, lower values flatten the barcode.
+ qrAspectRatio = 1.0f
+ }
+
+ /**
+ * @param aspectRatio defined by the standard: 3 to 5 for Pdf417 and MacroPdf417,
+ * 2 to 5 for MicroPdf417.
+ */
+ private fun pdf417Params(aspectRatio: Float) = Pdf417Params().apply {
+ pdf417EncodeMode = Pdf417EncodeMode.AUTO
+ pdf417ErrorLevel = Pdf417ErrorLevel.LEVEL2
+ pdf417ECIEncoding = ECIEncodings.UTF8
+ pdf417AspectRatio = aspectRatio
+ // 0 selects the column and row count automatically.
+ pdf417Columns = 0
+ pdf417Rows = 0
+ }
+
fun onBtnTakePhotoClick(@Suppress("UNUSED_PARAMETER") view: View) {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(packageManager) != null) {
diff --git a/codegen/Templates/android/build.mustache b/codegen/Templates/android/build.mustache
index 49d12289..ce6a2f53 100644
--- a/codegen/Templates/android/build.mustache
+++ b/codegen/Templates/android/build.mustache
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '9.1.0' apply false
- id 'com.android.library' version '9.1.0' apply false
+ id 'com.android.application' version '9.3.1' apply false
+ id 'com.android.library' version '9.3.1' apply false
}
diff --git a/codegen/Templates/android/gitignore.mustache b/codegen/Templates/android/gitignore.mustache
index db98ca2f..1dc6b9a9 100644
--- a/codegen/Templates/android/gitignore.mustache
+++ b/codegen/Templates/android/gitignore.mustache
@@ -20,6 +20,9 @@ out/
.gradle/
build/
+# Daemon JVM criteria, generated locally by the updateDaemonJvm task
+gradle/gradle-daemon-jvm.properties
+
# Local configuration file (sdk path, etc)
local.properties
diff --git a/codegen/Templates/android/manifest.mustache b/codegen/Templates/android/manifest.mustache
index 4268a9f9..6f138130 100644
--- a/codegen/Templates/android/manifest.mustache
+++ b/codegen/Templates/android/manifest.mustache
@@ -11,6 +11,7 @@
partial_header}}
-
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace {{packageName}}.{{modelPackage}}
-{
- ///
- /// Abstract base class for oneOf, anyOf schemas in the OpenAPI specification
- ///
- {{>visibility}} abstract partial class AbstractOpenAPISchema
- {
- ///
- /// Custom JSON serializer
- ///
- public static readonly JsonSerializerOptions SerializerOptions = new JsonSerializerOptions
- {
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- PropertyNameCaseInsensitive = false,
- // To ignore comments or trailing commas, set these:
- ReadCommentHandling = JsonCommentHandling.Disallow,
- UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
- AllowTrailingCommas = false,
- };
-
-
- ///
- /// Custom JSON serializer options for objects with additional properties.
- ///
- public static readonly JsonSerializerOptions AdditionalPropertiesSerializerOptions = new JsonSerializerOptions
- {
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- PropertyNameCaseInsensitive = false,
-
- ReadCommentHandling = JsonCommentHandling.Disallow,
- AllowTrailingCommas = false,
-
- };
-
- ///
- /// Gets or Sets the actual instance
- ///
- public abstract Object ActualInstance { get; set; }
-
- ///
- /// Gets or Sets IsNullable to indicate whether the instance is nullable
- ///
- public bool IsNullable { get; protected set; }
-
- ///
- /// Gets or Sets the schema type, which can be either `oneOf` or `anyOf`
- ///
- public string SchemaType { get; protected set; }
-
- ///
- /// Converts the instance into JSON string.
- ///
- public abstract string ToJson();
- }
-}
diff --git a/codegen/Templates/go/configuration.mustache b/codegen/Templates/go/configuration.mustache
index 369aee46..2dd7ad03 100644
--- a/codegen/Templates/go/configuration.mustache
+++ b/codegen/Templates/go/configuration.mustache
@@ -34,12 +34,6 @@ type BasicAuth struct {
Password string `json:"password,omitempty"`
}
-// APIKey provides API key based authentication to a request passed via context using ContextAPIKey
-type APIKey struct {
- Key string
- Prefix string
-}
-
// Configuration - API configuration
type Configuration struct {
BasePath string `json:"basePath,omitempty"`
diff --git a/codegen/Templates/java/pom.mustache b/codegen/Templates/java/pom.mustache
index 4189165d..98ccb2c2 100644
--- a/codegen/Templates/java/pom.mustache
+++ b/codegen/Templates/java/pom.mustache
@@ -275,7 +275,7 @@
${java.version}
${java.version}
2.14.0
- 5.3.2
+ 5.4.0
{{#joda}}
2.9.9
{{/joda}}
@@ -284,7 +284,7 @@
{{/threetenbp}}
1.0.0
4.13.2
- 0.8.14
+ 0.8.15
2.21.0
UTF-8
diff --git a/codegen/Templates/nodejs/package.mustache b/codegen/Templates/nodejs/package.mustache
index 2771148c..b99199b9 100644
--- a/codegen/Templates/nodejs/package.mustache
+++ b/codegen/Templates/nodejs/package.mustache
@@ -179,10 +179,10 @@
"@typescript-eslint/parser": "^8.40.0",
"eslint": "^9.33.0",
"eslint-config-prettier": "^10.1.8",
- "eslint-plugin-prettier": "^5.5.5",
+ "eslint-plugin-prettier": "^5.5.6",
"npm-check-updates": "^17.1.18",
- "prettier": "^3.8.3",
- "ts-jest": "^29.4.9",
+ "prettier": "^3.9.5",
+ "ts-jest": "^29.4.11",
"tslib": "^2.8.1",
"tsup": "^8.5.1"
},
diff --git a/codegen/Templates/php/Configuration.mustache b/codegen/Templates/php/Configuration.mustache
index 4b8d5ba0..6906bd24 100644
--- a/codegen/Templates/php/Configuration.mustache
+++ b/codegen/Templates/php/Configuration.mustache
@@ -51,8 +51,8 @@ class Configuration implements JsonSerializable
protected $authUrl = '{{#authMethods}}{{#-first}}{{tokenUrl}}{{/-first}}{{/authMethods}}';
/**
- * Version of API to use, possible values are v1, v1.1, v2, v3
- * default value is v1
+ * Versioned base path of the API (e.g. /v4.0)
+ *
* @var string
*/
protected $base_path = '{{basePathWithoutHost}}';
diff --git a/codegen/Templates/php/ObjectSerializer.mustache b/codegen/Templates/php/ObjectSerializer.mustache
index e52b5b64..ce4fad7e 100644
--- a/codegen/Templates/php/ObjectSerializer.mustache
+++ b/codegen/Templates/php/ObjectSerializer.mustache
@@ -4,7 +4,6 @@ namespace Aspose\BarCode;
use DateTime;
use DateTimeInterface;
-use Exception;
use SplFileObject;
class ObjectSerializer
@@ -204,7 +203,12 @@ class ObjectSerializer
if (!empty($data)) {
try {
return new DateTime($data);
- } catch (Exception $e) {}
+ } catch (\Throwable $e) {
+ // Invalid date string: treat it as a missing value and return null.
+ // Catch \Throwable (not just \Exception): on PHP 8.3+ with Xdebug loaded,
+ // decorating the thrown DateMalformedStringException with $xdebug_message
+ // raises an \Error, which is not an \Exception and would otherwise escape.
+ }
}
return null;
diff --git a/codegen/Templates/swift/AsposeBarcodeCloudClientError.mustache b/codegen/Templates/swift/AsposeBarcodeCloudClientError.mustache
index 704113b7..0be1de9a 100644
--- a/codegen/Templates/swift/AsposeBarcodeCloudClientError.mustache
+++ b/codegen/Templates/swift/AsposeBarcodeCloudClientError.mustache
@@ -21,7 +21,7 @@ public enum AsposeBarcodeCloudClientError: Error, CustomStringConvertible, @unch
}
return "Token request failed with status \(statusCode)"
case let .transportError(error):
- return error.localizedDescription
+ return String(describing: error)
}
}
}
diff --git a/codegen/Templates/swift/AsposeBarcodeCloudConfiguration.mustache b/codegen/Templates/swift/AsposeBarcodeCloudConfiguration.mustache
index 877fa176..031fa18b 100644
--- a/codegen/Templates/swift/AsposeBarcodeCloudConfiguration.mustache
+++ b/codegen/Templates/swift/AsposeBarcodeCloudConfiguration.mustache
@@ -26,7 +26,7 @@ public final class AsposeBarcodeCloudConfiguration: @unchecked Sendable {
tokenURL: String = AsposeBarcodeCloudConfiguration.defaultTokenURL,
sdkName: String = AsposeBarcodeCloudConfiguration.defaultSdkName,
sdkVersion: String = AsposeBarcodeCloudConfiguration.defaultSdkVersion,
- timeoutInterval: TimeInterval = 300
+ timeoutInterval: TimeInterval = 60
) {
self.accessToken = accessToken
self.clientId = clientId
diff --git a/codegen/Templates/swift/BarcodeAuthInterceptor.mustache b/codegen/Templates/swift/BarcodeAuthInterceptor.mustache
index a390f3ed..46746490 100644
--- a/codegen/Templates/swift/BarcodeAuthInterceptor.mustache
+++ b/codegen/Templates/swift/BarcodeAuthInterceptor.mustache
@@ -34,8 +34,12 @@ final class BarcodeAuthInterceptor: OpenAPIInterceptor, @unchecked Sendable {
requestBuilder: RequestBuilder,
completion: @Sendable @escaping (Result) -> Void
) {
+ let timeoutInterval = configuration.timeoutInterval
+
guard requestBuilder.requiresAuthentication else {
- completion(.success(urlRequest))
+ var modified = urlRequest
+ modified.timeoutInterval = timeoutInterval
+ completion(.success(modified))
return
}
@@ -43,6 +47,7 @@ final class BarcodeAuthInterceptor: OpenAPIInterceptor, @unchecked Sendable {
switch result {
case let .success(token):
var modified = urlRequest
+ modified.timeoutInterval = timeoutInterval
modified.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
completion(.success(modified))
case let .failure(error):
diff --git a/codegen/Templates/swift/Package.swift.mustache b/codegen/Templates/swift/Package.swift.mustache
index ccf3699c..8ac295e2 100644
--- a/codegen/Templates/swift/Package.swift.mustache
+++ b/codegen/Templates/swift/Package.swift.mustache
@@ -6,13 +6,13 @@ let package = Package(
name: "{{projectName}}",
platforms: [
{{#useVapor}}
- .macOS(.v10_15),
+ .macOS(.v12),
{{/useVapor}}
{{^useVapor}}
- .iOS(.v13),
- .macOS(.v10_15),
- .tvOS(.v13),
- .watchOS(.v6),
+ .iOS(.v15),
+ .macOS(.v12),
+ .tvOS(.v15),
+ .watchOS(.v8),
{{/useVapor}}
],
products: [
@@ -51,9 +51,14 @@ let package = Package(
path: "Examples/GenerateAndScan"
),
.testTarget(
- name: "{{projectName}}Tests",
+ name: "{{projectName}}UnitTests",
dependencies: ["{{projectName}}"],
- path: "Tests/{{projectName}}Tests"
+ path: "Tests/{{projectName}}UnitTests"
+ ),
+ .testTarget(
+ name: "{{projectName}}IntegrationTests",
+ dependencies: ["{{projectName}}"],
+ path: "Tests/{{projectName}}IntegrationTests"
),
],
swiftLanguageModes: [.v6]
diff --git a/codegen/Templates/swift/Podspec.mustache b/codegen/Templates/swift/Podspec.mustache
index 81a26263..862659fc 100644
--- a/codegen/Templates/swift/Podspec.mustache
+++ b/codegen/Templates/swift/Podspec.mustache
@@ -14,10 +14,10 @@ Pod::Spec.new do |s|
s.module_name = '{{projectName}}'
s.swift_versions = ['6.0']
- s.ios.deployment_target = '13.0'
- s.osx.deployment_target = '10.15'
- s.tvos.deployment_target = '13.0'
- s.watchos.deployment_target = '6.0'
+ s.ios.deployment_target = '15.0'
+ s.osx.deployment_target = '12.0'
+ s.tvos.deployment_target = '15.0'
+ s.watchos.deployment_target = '8.0'
s.source_files = 'Sources/{{projectName}}/**/*.swift'
s.requires_arc = true
end
diff --git a/codegen/Templates/swift/README.mustache b/codegen/Templates/swift/README.mustache
index 265e8559..1fc22f9d 100644
--- a/codegen/Templates/swift/README.mustache
+++ b/codegen/Templates/swift/README.mustache
@@ -5,8 +5,8 @@ This repository contains the Swift SDK for Aspose.BarCode Cloud.
## Requirements
- Swift Package Manager
-- iOS 13.0 or later
-- macOS 10.15 or later
+- iOS 15.0 or later
+- macOS 12.0 or later
## Usage
@@ -135,14 +135,21 @@ cp Tests/configuration.example.json Tests/configuration.json
make integration-test
```
-Alternatively, keep credentials in local environment variables:
+Alternatively, export credentials as environment variables:
```bash
-cp .env.integration.example .env.integration
-# Fill TEST_CONFIGURATION_CLIENT_ID and TEST_CONFIGURATION_CLIENT_SECRET, or TEST_CONFIGURATION_ACCESS_TOKEN.
+export TEST_CONFIGURATION_CLIENT_ID=''
+export TEST_CONFIGURATION_CLIENT_SECRET=''
+# Or export TEST_CONFIGURATION_ACCESS_TOKEN=''.
make integration-test
```
+Run documentation snippets with the same credentials:
+
+```bash
+make snippets-test
+```
+
Run the sample program:
```bash
diff --git a/codegen/Templates/swift/libraries/urlsession/URLSessionImplementations.mustache b/codegen/Templates/swift/libraries/urlsession/URLSessionImplementations.mustache
index d65200c9..702e3516 100644
--- a/codegen/Templates/swift/libraries/urlsession/URLSessionImplementations.mustache
+++ b/codegen/Templates/swift/libraries/urlsession/URLSessionImplementations.mustache
@@ -8,9 +8,6 @@ import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
-#if canImport(MobileCoreServices)
-import MobileCoreServices
-#endif
#if canImport(UniformTypeIdentifiers)
import UniformTypeIdentifiers
#endif
@@ -699,24 +696,12 @@ private class FormDataEncoding: ParameterEncoding {
func mimeType(for url: URL) -> String {
let pathExtension = url.pathExtension
- if #available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) {
- #if canImport(UniformTypeIdentifiers)
- if let utType = UTType(filenameExtension: pathExtension) {
- return utType.preferredMIMEType ?? "application/octet-stream"
- }
- return "application/octet-stream"
- #else
- return "application/octet-stream"
- #endif
- } else {
- #if canImport(MobileCoreServices)
- if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue(),
- let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
- return mimetype as String
- }
- #endif
- return "application/octet-stream"
+ #if canImport(UniformTypeIdentifiers)
+ if let utType = UTType(filenameExtension: pathExtension) {
+ return utType.preferredMIMEType ?? "application/octet-stream"
}
+ #endif
+ return "application/octet-stream"
}
}
diff --git a/codegen/config-android.json b/codegen/config-android.json
index 602ae7a4..d58e6ffe 100644
--- a/codegen/config-android.json
+++ b/codegen/config-android.json
@@ -4,18 +4,18 @@
"androidSdkVersion": "36",
"apiPackage": "com.aspose.barcode.cloud.demo_app",
"artifactId": "Android Application for Barcode Processing in the Cloud via REST API",
- "artifactVersion": "26.6.0",
+ "artifactVersion": "26.7.0",
"files": {
- "gradle.properties.mustache": {
- "destinationFilename": "gradle.properties",
+ "MainActivity.kt.mustache": {
+ "destinationFilename": "MainActivity.kt",
"templateType": "SupportingFiles"
},
"app-build.gradle.mustache": {
"destinationFilename": "app/build.gradle",
"templateType": "SupportingFiles"
},
- "MainActivity.kt.mustache": {
- "destinationFilename": "MainActivity.kt",
+ "gradle.properties.mustache": {
+ "destinationFilename": "gradle.properties",
"templateType": "SupportingFiles"
}
},
diff --git a/codegen/config-dart.json b/codegen/config-dart.json
index b41bdc2b..3f59fe00 100644
--- a/codegen/config-dart.json
+++ b/codegen/config-dart.json
@@ -3,6 +3,6 @@
"browserClient": false,
"pubDescription": "This SDK allows you to work with Aspose.BarCode for Cloud REST APIs in your Dart or Flutter applications quickly and easily",
"pubName": "aspose_barcode_cloud",
- "pubVersion": "4.26.6",
+ "pubVersion": "4.26.7",
"useEnumExtension": true
-}
\ No newline at end of file
+}
diff --git a/codegen/config-dotnet.json b/codegen/config-dotnet.json
index 921a34dc..57b9043a 100644
--- a/codegen/config-dotnet.json
+++ b/codegen/config-dotnet.json
@@ -1,15 +1,15 @@
{
"files": {
- "dependency.xml.mustache": {
- "destinationFilename": "dependency.xml",
- "templateType": "SupportingFiles"
- },
"NetFrameworkTests.csproj.mustache": {
"destinationFilename": "NetFrameworkTests.csproj",
"templateType": "SupportingFiles"
+ },
+ "dependency.xml.mustache": {
+ "destinationFilename": "dependency.xml",
+ "templateType": "SupportingFiles"
}
},
"packageName": "Aspose.BarCode.Cloud.Sdk",
- "packageVersion": "26.6.0",
+ "packageVersion": "26.7.0",
"targetFramework": "netstandard2.0"
}
diff --git a/codegen/config-go.json b/codegen/config-go.json
index 5516be3c..af809edc 100644
--- a/codegen/config-go.json
+++ b/codegen/config-go.json
@@ -6,5 +6,5 @@
}
},
"packageName": "barcode",
- "packageVersion": "4.2606.0"
+ "packageVersion": "4.2607.0"
}
diff --git a/codegen/config-java.json b/codegen/config-java.json
index ea7c8b81..8901d2d5 100644
--- a/codegen/config-java.json
+++ b/codegen/config-java.json
@@ -3,7 +3,7 @@
"artifactDescription": "Aspose.BarCode Cloud SDK for Java",
"artifactId": "aspose-barcode-cloud",
"artifactUrl": "https://www.aspose.cloud",
- "artifactVersion": "26.6.0",
+ "artifactVersion": "26.7.0",
"developerEmail": "denis.averin@aspose.com",
"developerName": "Denis Averin",
"developerOrganization": "Aspose",
diff --git a/codegen/config-node.json b/codegen/config-node.json
index c91a5fe4..184981bd 100644
--- a/codegen/config-node.json
+++ b/codegen/config-node.json
@@ -1,10 +1,7 @@
{
- "npmName": "aspose-barcode-cloud-node",
- "npmVersion": "26.6.0",
- "supportsES6": true,
"files": {
- "src/models.ts.mustache": {
- "destinationFilename": "src/models.ts",
+ "README.template.mustache": {
+ "destinationFilename": "README.template",
"templateType": "SupportingFiles"
},
"docs/index.md.mustache": {
@@ -15,9 +12,12 @@
"destinationFilename": "docs/models.md",
"templateType": "SupportingFiles"
},
- "README.template.mustache": {
- "destinationFilename": "README.template",
+ "src/models.ts.mustache": {
+ "destinationFilename": "src/models.ts",
"templateType": "SupportingFiles"
}
- }
+ },
+ "npmName": "aspose-barcode-cloud-node",
+ "npmVersion": "26.7.0",
+ "supportsES6": true
}
diff --git a/codegen/config-php.json b/codegen/config-php.json
index 3dc889fc..dd954c9d 100644
--- a/codegen/config-php.json
+++ b/codegen/config-php.json
@@ -1,4 +1,4 @@
{
- "artifactVersion": "26.6.0",
+ "artifactVersion": "26.7.0",
"invokerPackage": "Aspose\\BarCode"
-}
\ No newline at end of file
+}
diff --git a/codegen/config-python.json b/codegen/config-python.json
index 0d43ba62..0e58bdbd 100644
--- a/codegen/config-python.json
+++ b/codegen/config-python.json
@@ -1,6 +1,6 @@
{
"packageName": "aspose_barcode_cloud",
"packageUrl": "https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Python",
- "packageVersion": "26.6.0",
+ "packageVersion": "26.7.0",
"projectName": "aspose-barcode-cloud"
-}
\ No newline at end of file
+}
diff --git a/codegen/config-swift.json b/codegen/config-swift.json
index 6cff1407..b2d6ba50 100644
--- a/codegen/config-swift.json
+++ b/codegen/config-swift.json
@@ -26,10 +26,10 @@
"hideGenerationTimestamp": true,
"library": "urlsession",
"mapFileBinaryToData": true,
- "packageVersion": "26.6.0",
+ "packageVersion": "26.7.0",
"projectName": "AsposeBarcodeCloud",
"responseAs": "AsyncAwait,ObjcBlock",
"swiftPackagePath": "Sources/AsposeBarcodeCloud",
"useClasses": true,
"useSPMFileStructure": true
-}
\ No newline at end of file
+}
diff --git a/codegen/generate-dotnet.bash b/codegen/generate-dotnet.bash
index 06f9c978..a38fb9f8 100755
--- a/codegen/generate-dotnet.bash
+++ b/codegen/generate-dotnet.bash
@@ -27,6 +27,8 @@ cp ../LICENSE "$targetDir/src/LICENSE.txt"
cp ../scripts/check-badges.bash "$targetDir/scripts/"
rm -rf "$targetDir/src/Model/"
+# AbstractOpenAPISchema: oneOf/anyOf base class this spec never uses (no polymorphic models).
+rm -f "$tempDir/src/Aspose.BarCode.Cloud.Sdk/Model/AbstractOpenAPISchema.cs"
mv "$tempDir/src/Aspose.BarCode.Cloud.Sdk/Model" "$targetDir/src"
rm -rf $targetDir/src/Api/*Api.cs
diff --git a/codegen/generate-java.bash b/codegen/generate-java.bash
index 8b746058..3b6ae7af 100755
--- a/codegen/generate-java.bash
+++ b/codegen/generate-java.bash
@@ -24,6 +24,8 @@ python Tools/split-java-file.py $tempDir/src/main/java/com/aspose/barcode/cloud/
rm -rf $targetDir/src/main/java/com/aspose/barcode/cloud/api/*
mv $tempDir/src/main/java/com/aspose/barcode/cloud/api/* $targetDir/src/main/java/com/aspose/barcode/cloud/api
rm -rf $targetDir/src/main/java/com/aspose/barcode/cloud/model/*
+# AbstractOpenApiSchema: oneOf/anyOf scaffolding this spec never uses (no polymorphic models).
+rm -f "$tempDir/src/main/java/com/aspose/barcode/cloud/model/AbstractOpenApiSchema.java"
mv $tempDir/src/main/java/com/aspose/barcode/cloud/model/* $targetDir/src/main/java/com/aspose/barcode/cloud/model
rm -rf $targetDir/src/main/java/com/aspose/barcode/cloud/requests/*
mv $tempDir/src/main/java/com/aspose/barcode/cloud/requests/* $targetDir/src/main/java/com/aspose/barcode/cloud/requests
@@ -31,6 +33,9 @@ rm -f $targetDir/src/main/java/com/aspose/barcode/cloud/*.java
# Ignore some files
rm "$tempDir/src/main/java/com/aspose/barcode/cloud/GzipRequestInterceptor.java"
rm "$tempDir/src/main/java/com/aspose/barcode/cloud/StringUtil.java"
+# ServerConfiguration/ServerVariable: multi-server URL scaffolding; ApiClient uses a single fixed base path.
+rm "$tempDir/src/main/java/com/aspose/barcode/cloud/ServerConfiguration.java"
+rm "$tempDir/src/main/java/com/aspose/barcode/cloud/ServerVariable.java"
mv $tempDir/src/main/java/com/aspose/barcode/cloud/*.java $targetDir/src/main/java/com/aspose/barcode/cloud
rm -rf ${targetDir}/docs/*
diff --git a/codegen/generate-php.bash b/codegen/generate-php.bash
index 05c10c7b..2727bda0 100755
--- a/codegen/generate-php.bash
+++ b/codegen/generate-php.bash
@@ -27,6 +27,9 @@ rm -f "${tempDir}/lib/FormDataProcessor.php"
mv "${tempDir}/lib/"*.php "${targetDir}/src/Aspose/BarCode"
rm -f "${targetDir}/src/Aspose/BarCode/Model/"*
+# Drop ModelInterface.php: an orphan swagger-codegen interface that no generated model
+# implements (models declare only ArrayAccess). Unused; keeping it ships dead surface.
+rm -f "${tempDir}/lib/Model/ModelInterface.php"
mv "${tempDir}/lib/Model/"* "${targetDir}/src/Aspose/BarCode/Model"
rm -f "${targetDir}/src/Aspose/BarCode/Requests/"*
diff --git a/codegen/generate-python.bash b/codegen/generate-python.bash
index 9b94e2f0..f0468ba0 100755
--- a/codegen/generate-python.bash
+++ b/codegen/generate-python.bash
@@ -28,6 +28,12 @@ java -jar Tools/openapi-generator-cli.jar generate -i "$specSource" -g python -t
rm -rf $targetDir/aspose_barcode_cloud/*
cp -r $tempDir/aspose_barcode_cloud/* $targetDir/aspose_barcode_cloud/
+# Drop orphan modules emitted by the upstream python generator template that this
+# SDK does not use: exceptions.py (this SDK raises aspose_barcode_cloud.rest.ApiException
+# instead) and api_response.py (imports pydantic, which is not a dependency). Nothing
+# imports either module; keeping them only breaks `import` and dilutes coverage.
+rm -f "$targetDir/aspose_barcode_cloud/exceptions.py" "$targetDir/aspose_barcode_cloud/api_response.py"
+
rm -rf $targetDir/docs/*
cp $tempDir/docs/* $targetDir/docs/
diff --git a/doc/ReleaseWorkflow.md b/doc/ReleaseWorkflow.md
index fe14909a..ea93303d 100644
--- a/doc/ReleaseWorkflow.md
+++ b/doc/ReleaseWorkflow.md
@@ -10,22 +10,37 @@ Debian packages: list in file `./doc/deb_packages.list`. To install run `./scrip
Steps:
1. Call `./scripts/start-release.bash`
- It will create new branches for each submodule
- And update versions in `config*.json`.
+ It will refresh the API spec (`spec/aspose-barcode-cloud.json`),
+ create a `release-` branch for each submodule,
+ and update versions in `config*.json`.
2. Run `make-all.cmd`
It will generate all SDKs with updated version and template.
-3. Update secret and set new value for `TEST_CONFIGURATION_ACCESS_TOKEN`. It should be updatead less than 24 hours ago.
+3. Update the organization secret
+ [`TEST_CONFIGURATION_ACCESS_TOKEN`](https://github.com/organizations/aspose-barcode-cloud/settings/secrets/actions).
+ Refresh it less than 24 hours before running SDK integration tests.
-4. Review all changes in submodules and push PR to GitHub.
+4. Review all changes in submodules and open a draft PR on GitHub for each SDK,
+ from its `release-` branch into that SDK's default branch.
-5. Merge all successfull PRs to main.
+5. Merge all successful PRs into each SDK's default branch — `main` for every
+ SDK except `go`, whose default branch is `v4` (see [branches.md](branches.md)).
-6. Create release tags vYY.MM. Put attention to go, dart and other SDK with SemVer 0.x versions.
+6. Phase 2: create the release tag using the SDK-specific version convention
+ and create a GitHub prerelease for that tag. Pay special attention to Go,
+ Dart, and other SDKs whose tags do not use the standard `vYY.MM.P` form.
-7. Publish new released packages to appropriate repository (NuGet, Maven, PyPi, NPM ...).
+7. Publish the packages to their appropriate registries (NuGet, the Aspose
+ Java repository, npm, PyPI, Packagist, pub.dev, Go modules). Packages are
+ signed at publish time as a per-registry concern — e.g. NuGet packages are
+ signed with a code-signing certificate — so confirm the signing setup for
+ each registry.
-8. After package was published crate Release on GitHub. With changelog and release notes.
+8. Phase 3: after the package is available, promote the GitHub prerelease to
+ the stable latest release. Include the changelog and release notes.
9. Link all updated branches and check `codegen` produces the same output.
+
+See [versioning.md](versioning.md) for the version scheme and
+[branches.md](branches.md) for the per-SDK repositories and default branches.
diff --git a/doc/api-design.md b/doc/api-design.md
new file mode 100644
index 00000000..9f97e12f
--- /dev/null
+++ b/doc/api-design.md
@@ -0,0 +1,44 @@
+API design (no code smells)
+===========================
+
+Requirement
+-----------
+
+> API does not have "code smells" e.g. do not pass long list of parameters.
+
+(Cloud SDK Requirements → Code quality.)
+
+How this repository satisfies it
+--------------------------------
+
+Operations with many optional parameters group them into **typed parameter
+objects** instead of a long, flat argument list. For the `generate` family the
+options are grouped by concern:
+
+* `BarcodeImageParams` — image format, size, colours, resolution, text location …
+* `QrParams`, `Code128Params`, `Pdf417Params` — per-symbology options
+
+so a call takes a few grouped objects rather than dozens of positional
+arguments. Swift example:
+
+```swift
+GenerateAPI.generate(
+ barcodeType: .qr,
+ data: "Aspose.BarCode Cloud",
+ barcodeImageParams: BarcodeImageParams(imageFormat: .png),
+ qrParams: QrParams(qrErrorLevel: .levelM),
+ apiConfiguration: client.apiConfiguration
+)
+```
+
+The grouping is driven from the spec and applied during generation (see the
+`Split generate params into groups` change). The generated doc comments label
+each argument as `Grouped parameters`, e.g.:
+
+```
+- parameter barcodeImageParams: (BarcodeImageParams) Grouped barcodeImageParams parameters.
+- parameter qrParams: (QrParams) Grouped qrParams parameters.
+```
+
+Keeping parameters grouped in the spec is what keeps every generated SDK free of
+the long-parameter-list smell.
diff --git a/doc/authentication.md b/doc/authentication.md
new file mode 100644
index 00000000..8bc76f34
--- /dev/null
+++ b/doc/authentication.md
@@ -0,0 +1,31 @@
+Authentication
+==============
+
+Requirement
+-----------
+
+> Use OAuth protocol for authentication.
+
+(Cloud SDK Requirements → Implementation details.)
+
+How this repository satisfies it
+--------------------------------
+
+Each SDK authenticates with **OAuth 2 client credentials**. The client is
+configured with a `clientId` and `clientSecret`, exchanges them for a bearer
+access token at the token endpoint, and then sends `Authorization: Bearer
+` on each request.
+
+Defaults (Swift `AsposeBarcodeCloudConfiguration`, mirrored in the other SDKs):
+
+* token URL — `https://id.aspose.cloud/connect/token`
+* host — `https://api.aspose.cloud/v4.0`
+
+The access token is fetched **lazily** on the first API call; an explicit
+warm-up call is also available (e.g. Swift `client.authorize()`). A dedicated
+auth interceptor injects the `Authorization` header on each authenticated
+request.
+
+If a caller already holds a token, it can configure the SDK with an
+`accessToken` directly and skip the client-credentials exchange entirely. The
+full settings list is in [configuration.md](configuration.md).
diff --git a/doc/branches.md b/doc/branches.md
new file mode 100644
index 00000000..24944927
--- /dev/null
+++ b/doc/branches.md
@@ -0,0 +1,27 @@
+SDK repository branches
+=======================
+
+Each SDK submodule is a standalone GitHub repository under the `aspose-barcode-cloud` org with its own
+default (mainline) branch. The default branch is `main` for every SDK **except `go`, whose default is
+`v4`** — go's `main` is a deprecated 24.9-era branch, so base go work and open go pull requests against
+`v4`, not `main`.
+
+| SDK | Repository | Default (base) branch |
+|-----|------------|-----------------------|
+| android | [`Aspose.BarCode-Cloud-SDK-for-Android`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Android) | `main` |
+| dart | [`Aspose.BarCode-Cloud-SDK-for-Dart`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Dart) | `main` |
+| dotnet | [`Aspose.BarCode-Cloud-SDK-for-.NET`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-.NET) | `main` |
+| go | [`aspose-barcode-cloud-go`](https://github.com/aspose-barcode-cloud/aspose-barcode-cloud-go) | `v4` |
+| java | [`Aspose.BarCode-Cloud-SDK-for-Java`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Java) | `main` |
+| node | [`Aspose.BarCode-Cloud-SDK-for-Node.js`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Node.js) | `main` |
+| php | [`Aspose.BarCode-Cloud-SDK-for-PHP`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-PHP) | `main` |
+| python | [`Aspose.BarCode-Cloud-SDK-for-Python`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Python) | `main` |
+| swift | [`Aspose.BarCode-Cloud-SDK-for-Swift`](https://github.com/aspose-barcode-cloud/Aspose.BarCode-Cloud-SDK-for-Swift) | `main` |
+
+Release work lands on a `release-.` branch (e.g.
+`release-26.7`) cut from the default branch. If the release has a non-zero
+patch component, include it in the branch name (e.g. `release-26.7.1`).
+
+Open the release branch as a **draft** pull request back into the SDK's
+default branch. Use the SDK's full package version in the title, for example
+`Release 26.7.0`.
diff --git a/doc/cloud-sdk-requirements.md b/doc/cloud-sdk-requirements.md
new file mode 100644
index 00000000..740a617f
--- /dev/null
+++ b/doc/cloud-sdk-requirements.md
@@ -0,0 +1,25 @@
+Cloud SDK Requirements
+======================
+
+Aspose's Cloud SDK requirements, split into one document per requirement and
+mapped to how **this** codegen repository satisfies each one.
+
+Source: the legacy internal wiki page "Cloud SDK Requirements". Where the
+original wording is obsolete it is updated to current reality — for example
+the OAuth 1 `AppSID`/`AppKey` credentials are now OAuth 2
+`clientId`/`clientSecret`, and the base URL is now
+`https://api.aspose.cloud/v4.0`.
+
+| Requirement | Document |
+|-------------|----------|
+| Code quality — formatting | [formatting.md](formatting.md) |
+| Code quality — documentation comments | [documentation.md](documentation.md) |
+| Code quality — API design (no code smells) | [api-design.md](api-design.md) |
+| Implementation — authentication | [authentication.md](authentication.md) |
+| Implementation — client configuration & identification | [configuration.md](configuration.md) |
+| Versioning | [versioning.md](versioning.md) |
+| Release process | [ReleaseWorkflow.md](ReleaseWorkflow.md) |
+
+Each document has the same shape: the **requirement** as originally stated, then
+**how this repository satisfies it**, grounded in the actual generate scripts,
+templates, and `make` targets.
diff --git a/doc/configuration.md b/doc/configuration.md
new file mode 100644
index 00000000..a43c98b9
--- /dev/null
+++ b/doc/configuration.md
@@ -0,0 +1,43 @@
+Client configuration and identification
+=======================================
+
+Requirement
+-----------
+
+> * Set `x-aspose-client` header to indicate language and version of the SDK.
+> * SDK API Client has configuration settings: `AppSID`, `AppKey`, `baseURI`,
+> `version`, `debug`.
+
+(Cloud SDK Requirements → Implementation details.)
+
+Client identification headers
+-----------------------------
+
+Every SDK sets two headers on each request; the names live in the language
+templates under `codegen/Templates//`:
+
+* `x-aspose-client` — the SDK name (e.g. `swift sdk`, `nodejs sdk`)
+* `x-aspose-client-version` — the SDK package version
+
+These are driven by the configuration's `sdkName` / `sdkVersion` (or the package
+version constant), so they stay in sync with the released version automatically.
+
+Configuration settings
+----------------------
+
+| Setting | Notes |
+|---------|-------|
+| `clientId` | OAuth 2 client id; required when `accessToken` is not supplied |
+| `clientSecret` | OAuth 2 client secret; required when `accessToken` is not supplied |
+| `host` (optional) | defaults to `https://api.aspose.cloud/v4.0`; override for private cloud / test |
+| `debug` (optional, per-SDK) | e.g. .NET debug logging; not a universal flag |
+
+Additional settings exposed by the client configuration:
+
+* `accessToken` — supply a token directly and skip the OAuth exchange
+* `tokenURL` — OAuth token endpoint (`https://id.aspose.cloud/connect/token`)
+* `sdkName` / `sdkVersion` — feed the `x-aspose-client*` headers
+* request timeout — e.g. Swift `timeoutInterval` (default 60s)
+
+See [authentication.md](authentication.md) for how `clientId`/`clientSecret`
+become a bearer token.
diff --git a/doc/documentation.md b/doc/documentation.md
new file mode 100644
index 00000000..e75bf297
--- /dev/null
+++ b/doc/documentation.md
@@ -0,0 +1,35 @@
+Documentation comments
+======================
+
+Requirement
+-----------
+
+> All methods and data objects have autogenerated comments.
+
+(Cloud SDK Requirements → Code quality.)
+
+How this repository satisfies it
+--------------------------------
+
+Doc comments are generated by the OpenAPI Generator from the API description in
+`spec/aspose-barcode-cloud.json`. Each operation and model in the spec carries a
+summary/description that the language templates render as native doc comments:
+
+* Swift / C# — `///` documentation comments
+* Python — docstrings
+* Java — Javadoc
+* Node/TypeScript — JSDoc/TSDoc
+* Go / PHP / Dart — language-native doc comments
+
+In addition, every `generate-.bash` regenerates a Markdown `docs/` folder
+(API + model reference) into the submodule, e.g. for Swift:
+
+```bash
+rm -rf "$targetDir/docs"
+mv "$tempDir/docs" "$targetDir/docs"
+```
+
+Because both the inline comments and the `docs/` reference come from the spec,
+keeping the spec's operation/model/parameter descriptions complete is what keeps
+the generated documentation complete. To change wording, edit the spec (or the
+language template in `codegen/Templates//`), not the generated output.
diff --git a/doc/formatting.md b/doc/formatting.md
new file mode 100644
index 00000000..149b0842
--- /dev/null
+++ b/doc/formatting.md
@@ -0,0 +1,52 @@
+Code formatting
+===============
+
+Requirement
+-----------
+
+> Follows code formatting conventions of target language.
+
+(Cloud SDK Requirements → Code quality.)
+
+How this repository satisfies it
+--------------------------------
+
+Every SDK generator except the Android demo app ends by running
+`make after-gen` inside the SDK submodule:
+
+```bash
+pushd "$targetDir" && make after-gen && popd >/dev/null
+```
+
+`after-gen`'s `format` step runs the language's canonical formatter over the
+**whole tree** — generated source *and* hand-written tests:
+
+| SDK | `make format` runs |
+|--------|--------------------|
+| Dart | `dart format .` |
+| Swift | `swiftformat .` |
+| Python | `black --line-length=120 … tests/ scripts/ snippets/ …` |
+| .NET | `dotnet format` |
+| Go | `scripts/format.sh` (`gofmt`) |
+| Java | `scripts/format.bash` |
+| Node | `npm run format` (Prettier) |
+| PHP | php-cs-fixer |
+
+For these SDKs, `make after-gen` formats everything, so freshly generated code
+is in the formatter's canonical form. Android is excluded because this
+repository generates a demo app for it rather than an SDK package.
+
+Hand-written tests and generation
+----------------------------------
+
+Test files are **hand-written**, not generated — regeneration never overwrites
+them. But the `format` step above *does* reformat them. So tests must be
+committed formatter-clean; otherwise the next `make ` re-dirties them and
+leaves spurious unstaged changes.
+
+Check a tree without modifying it, e.g. for Swift:
+
+```bash
+cd submodules/swift && swiftformat --lint .
+# 0/N files require formatting → clean
+```
diff --git a/doc/test-coverage-requirements.md b/doc/test-coverage-requirements.md
new file mode 100644
index 00000000..28e946c8
--- /dev/null
+++ b/doc/test-coverage-requirements.md
@@ -0,0 +1,15 @@
+# Test coverage requirements
+
+Every generated SDK must satisfy two coverage bars. `submodules/android` is exempt from both — it is a
+demo application that consumes the published Java SDK, so it has no API surface of its own to cover.
+
+1. **API endpoint coverage — at least 80%.** At least 80% of the API operations defined in
+ `spec/aspose-barcode-cloud.json` must be exercised by a test (no fewer than 8 of the 9 at the current
+ surface). Every SDK currently covers all 9 operations (100%).
+2. **Line code coverage — at least 80%, enforced by a per-SDK CI gate.** Each SDK's own CI fails the build
+ when line coverage drops below 80%. The bar is reached with deterministic **offline** tests over the
+ generated model / enum / serialization / core code (mirroring java's `GeneratedModelCoverageTest` and
+ `SdkCoreCoverageTest`) on top of the live API tests, which run in CI via the
+ `TEST_CONFIGURATION_ACCESS_TOKEN` secret.
+
+Regenerating or changing an SDK must not drop either metric below 80%.
diff --git a/doc/versioning.md b/doc/versioning.md
new file mode 100644
index 00000000..9f002aea
--- /dev/null
+++ b/doc/versioning.md
@@ -0,0 +1,91 @@
+Versioning
+==========
+
+The release pipeline uses **calendar versioning** (`YY.M.P`) as the canonical input:
+year, month, and an optional patch. Each language SDK then transforms that input
+into the format its package ecosystem expects.
+
+The transform lives in [`scripts/new-version.py`](../scripts/new-version.py); the
+release entry point [`scripts/start-release.bash`](../scripts/start-release.bash)
+calls it with the current year and month.
+
+Per-language formats
+--------------------
+
+Patch defaults to `0`. Examples below assume input `(26, 4, 0)` and a second row
+for `(26, 4, 1)` where the patch matters.
+
+| SDK | Config field | Format | Example for (26, 4, 0) | Example for (26, 4, 1) | Registry convention |
+|---------|--------------------|-------------------|------------------------|------------------------|---------------------|
+| .NET | `packageVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | NuGet — loose SemVer-ish |
+| Android | `artifactVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | Maven coordinates — no enforced range syntax |
+| Dart | `pubVersion` | `4.YY.M[+P]` | `4.26.4` | `4.26.4+1` | pub.dev — strict SemVer 2.0.0 |
+| Go | `packageVersion` | `4.YYMM.P` | `4.2604.0` | `4.2604.1` | Go modules — strict SemVer; major in import path for major ≥ 2 |
+| Java | `artifactVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | Maven Central — loose-ish |
+| Node | `npmVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | npm — strict SemVer 2.0.0 |
+| PHP | `artifactVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | Packagist — strict SemVer-ish |
+| Python | `packageVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | PyPI — [PEP 440](https://peps.python.org/pep-0440/) |
+| Swift | `packageVersion` | `YY.M.P` | `26.4.0` | `26.4.1` | SwiftPM — strict [SemVer 2.0.0](https://semver.org/) |
+
+Why some SDKs get a synthetic `4.` major
+-----------------------------------------
+
+The Aspose.BarCode Cloud HTTP API is at version 4. Some package registries enforce
+strict SemVer and refuse to publish "breaking" major-version bumps without
+ceremony, so for those ecosystems we keep the registry-visible major locked at
+`4` and put the calendar coordinates into the lower positions:
+
+* **Go** (`4.YYMM.P`) — Go modules treat the major version as load-bearing.
+ Any major ≥ 2 must be reflected in the import path (e.g. `…/v4`). Bumping the
+ major every year would force every consumer to rewrite their imports. We bake
+ `4.` in and squash year+month into the minor (`YYMM`).
+* **Dart** (`4.YY.M[+P]`) — pub.dev enforces SemVer 2.0.0; a `4.x.x` line gives
+ consumers a stable major. The patch component piggybacks on SemVer build
+ metadata (`+1`, `+2`, …) so it stays an in-range upgrade.
+
+The remaining SDKs (.NET, Android, Java, Node, PHP, Python, Swift) ship the raw
+calendar version `YY.M.P` into the registry's `MAJOR.MINOR.PATCH` slots.
+
+Why the raw-calendar SDKs work even with strict SemVer tooling
+--------------------------------------------------------------
+
+Treating the year as the major works mechanically — every January is a "major"
+bump, which is exactly the signal SemVer tools encode when the major number
+changes. Consumers using range syntax (`^26.4.0`, `from: "26.4.0"`, `~> 26.4`)
+get bug fixes and minor updates within the current year for free, but **must
+explicitly bump their constraint at the year boundary** to receive the next
+year's releases. That is consistent across the seven raw-calendar SDKs.
+
+Swift specifics
+---------------
+
+Swift Package Manager uses **SemVer 2.0.0** exclusively for version
+requirements (see
+[apple/swift-package-manager — PackageDescription](https://docs.swift.org/package-manager/PackageDescription/PackageDescription.html#package-dependency-requirement)):
+
+* Format: `MAJOR.MINOR.PATCH`, optional `-prerelease` and `+buildmetadata`.
+* Versions are resolved from **git tags**. SwiftPM strips a leading `v` from
+ tag names, so both `v26.4.0` and `26.4.0` resolve to the same version.
+* `.package(url: …, from: "26.4.0")` means `>= 26.4.0, < 27.0.0` — same-major
+ upgrades only.
+* This repo uses **`vYY.M.P`** as the tag form (e.g. `v26.4.0`) to match the
+ other SDKs, and the Swift README documents that consumers drop the `v` when
+ writing `from:`.
+
+Implication for Swift consumers: a `from: "26.4.0"` constraint will keep
+receiving `26.4.x` and `26.5.x` updates throughout 2026, but **will not pull
+`27.x` automatically** when the year rolls over. This matches the behaviour of
+the other raw-calendar SDKs above.
+
+Release flow
+------------
+
+`scripts/start-release.bash`:
+
+1. Reads current year (`YY`) and month (`M`); patch defaults to `0`.
+2. Calls `scripts/new-version.py YY M` which rewrites every `config-*.json`
+ using the per-language transform above.
+3. The per-language `make ` target regenerates the submodule from the
+ updated config.
+
+To override patch, call `new-version.py` directly: `python scripts/new-version.py 26 4 1`.
diff --git a/scripts/check-urls.py b/scripts/check-urls.py
index 76b7a9be..21549042 100644
--- a/scripts/check-urls.py
+++ b/scripts/check-urls.py
@@ -55,6 +55,8 @@
".curl.se",
".dart.dev",
".dartlang.org",
+ ".example.com",
+ ".example.test",
".example",
".getcomposer.org",
".go.dev",
@@ -85,7 +87,6 @@
# Regular domains
"barcode.qa.aspose.cloud",
"editorconfig.org",
- "example.com",
]
)
diff --git a/scripts/new-version.py b/scripts/new-version.py
index 89bb4205..9ae8bdd2 100755
--- a/scripts/new-version.py
+++ b/scripts/new-version.py
@@ -3,6 +3,7 @@
from __future__ import division, print_function
import argparse
+import collections
import json
import os
import sys
@@ -101,15 +102,19 @@ def set_swift_version(new_version, filename=os.path.join(BASE_CONFIG_DIR, "confi
def read_config(filename):
+ # Preserve the on-disk key order so a version bump round-trips to a minimal,
+ # stable diff instead of reshuffling hand-ordered keys.
with open(filename, "rb") as rf:
- config = json.load(rf)
+ config = json.load(rf, object_pairs_hook=collections.OrderedDict)
return config
def save_config(config, filename):
+ # Keep key order (no sort_keys) and always end with a single trailing newline
+ # so releases produce stable, POSIX-friendly JSON.
with open(filename, "wb") as wf:
- string = json.dumps(config, indent=4, separators=(",", ": "), sort_keys=True)
- wf.write(string.replace("\r", "").encode("utf-8"))
+ string = json.dumps(config, indent=4, separators=(",", ": "))
+ wf.write((string.replace("\r", "") + "\n").encode("utf-8"))
def main(new_versions):
diff --git a/submodules/android b/submodules/android
index 5954e078..9697b0eb 160000
--- a/submodules/android
+++ b/submodules/android
@@ -1 +1 @@
-Subproject commit 5954e0780bae1a16042efafc92d2d11f7614ee50
+Subproject commit 9697b0eb527228b1be2a0b0f48007e262f56f1db
diff --git a/submodules/dart b/submodules/dart
index e81c31aa..9290fce4 160000
--- a/submodules/dart
+++ b/submodules/dart
@@ -1 +1 @@
-Subproject commit e81c31aa691a96f2a7e2453427641d2a356dde3d
+Subproject commit 9290fce4b40634c4a5453527270facdd7dc68339
diff --git a/submodules/dotnet b/submodules/dotnet
index a0ffc401..ea20ddb3 160000
--- a/submodules/dotnet
+++ b/submodules/dotnet
@@ -1 +1 @@
-Subproject commit a0ffc40167de17dceb159b87ab8db17f86449391
+Subproject commit ea20ddb311da0df5c1d83ee3fe89cf8d8bc8491f
diff --git a/submodules/go b/submodules/go
index 41a31ca5..95182944 160000
--- a/submodules/go
+++ b/submodules/go
@@ -1 +1 @@
-Subproject commit 41a31ca59ba79dda709dc60413a18f0e7078e75f
+Subproject commit 9518294462ffc69f84403041fd8d20426c3815ce
diff --git a/submodules/java b/submodules/java
index 04b84be6..72045348 160000
--- a/submodules/java
+++ b/submodules/java
@@ -1 +1 @@
-Subproject commit 04b84be6879a3c7592061f121af033ab30cd7ca7
+Subproject commit 720453487a74755da658319d3cfe33f1b8a2678f
diff --git a/submodules/node b/submodules/node
index 3be091b4..b51e1d97 160000
--- a/submodules/node
+++ b/submodules/node
@@ -1 +1 @@
-Subproject commit 3be091b4a9f74153b0d6bc48f3b4eb90f5ea6dbe
+Subproject commit b51e1d971d877a539d06300ab8cbb2bb433a3f2d
diff --git a/submodules/php b/submodules/php
index 58f20070..01f11777 160000
--- a/submodules/php
+++ b/submodules/php
@@ -1 +1 @@
-Subproject commit 58f20070f074e3572f6d97c87739ebcd406d5008
+Subproject commit 01f11777d121e9db4eb0fdd3cd1306388b9febef
diff --git a/submodules/python b/submodules/python
index 4b074aa4..719f4b8c 160000
--- a/submodules/python
+++ b/submodules/python
@@ -1 +1 @@
-Subproject commit 4b074aa48a5c937ab63b7b5634988038e845dc75
+Subproject commit 719f4b8c1d9d38fe183d0fc269d82e60d07f0603
diff --git a/submodules/swift b/submodules/swift
index 11db3c5c..f69d5bdc 160000
--- a/submodules/swift
+++ b/submodules/swift
@@ -1 +1 @@
-Subproject commit 11db3c5c5fbb53e34596668944bd71d803777ee8
+Subproject commit f69d5bdc8c1ac9e8b82c3a767ee201624adab4ba