Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions .github/workflows/generate-aidocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: Generate AI Docs

# ──────────────────────────────────────────────────────────────────────────────
# Triggers
# ──────────────────────────────────────────────────────────────────────────────
on:
# Manual run only: operator supplies the release version label
workflow_dispatch:
inputs:
version:
description: 'Release version for the output branch (e.g. v2.5.0)'
required: true
type: string
force_regenerate:
description: 'Force full regeneration'
required: false
type: boolean
default: false

# ──────────────────────────────────────────────────────────────────────────────
# Jobs
# ──────────────────────────────────────────────────────────────────────────────
jobs:
generate-aidocs:
name: Generate AI Documentation
# C2M4AI.exe is a Windows x64 binary — must use a Windows runner
runs-on: windows-latest
permissions:
contents: write # needed to push the versioned branch

steps:
# ── 1. Checkout ──────────────────────────────────────────────────────────
- name: Checkout repository
uses: actions/checkout@v4
with:
# fetch-depth 2 gives us HEAD and HEAD~1 for incremental spec diffing
fetch-depth: 2
token: ${{ secrets.GITHUB_TOKEN }}

# ── 2. Resolve release version ───────────────────────────────────────────
- name: Resolve release version
id: version
shell: pwsh
run: |
if ('${{ github.event_name }}' -eq 'workflow_dispatch') {
$v = '${{ inputs.version }}'
} else {
# Auto-derive from api.json info.version for push and workflow_run triggers
$spec = Get-Content articles/LCPublicAPI/api/Public-API.v1.json | ConvertFrom-Json
$v = "v$($spec.info.version)"
}
Write-Host "Release version: $v"
"version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8

# ── 3. Download C2M4AI.exe ────────────────────────────────────────────────
- name: Download C2M4AI
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path tools | Out-Null
.\pipeline\Get-C2M4AI.ps1 -OutputPath tools\C2M4AI.exe

# ── 4. Download .NET SDK XML documentation ───────────────────────────────
- name: Download .NET SDK XML documentation
shell: pwsh
run: .\pipeline\Get-DotNetSdkXml.ps1 -OutputPath tools\Rws.LanguageCloud.Sdk.xml

# ── 5. Download Java SDK sources ─────────────────────────────────────────
- name: Download Java SDK sources
shell: pwsh
run: .\pipeline\Get-JavaSdkSources.ps1 -OutputDir tools\java-api

# ── 6. Save previous api.json for incremental spec diffing ───────────────
- name: Save previous api.json
id: prev_spec
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path tools | Out-Null
try {
# git show exits non-zero when there is no previous commit (first push)
git show HEAD~1:articles/LCPublicAPI/api/Public-API.v1.json | Set-Content tools\prev-api.json -NoNewline
Write-Host "Previous spec saved to tools\prev-api.json"
"exists=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
} catch {
Write-Host "No previous commit available — will do full regeneration."
"exists=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
}

# ── 7. Run the AI Docs Pipeline ───────────────────────────────────────────
- name: Run AI Docs Pipeline
shell: pwsh
run: |
$params = @{
RootDir = '.'
}

$forceRegen = '${{ inputs.force_regenerate }}' -eq 'true'
$hasPrevSpec = '${{ steps.prev_spec.outputs.exists }}' -eq 'true'
$isManual = '${{ github.event_name }}' -eq 'workflow_dispatch'

if ($forceRegen) {
# Explicit full rebuild: ignore hashes and re-write every file
$params.All = $true
$params.Force = $true
} elseif ($isManual -or -not $hasPrevSpec) {
# Manual run without force, or first-ever commit: full rebuild
$params.All = $true
} else {
# Push-triggered incremental: pass changed files for smart routing
$changed = @(git diff --name-only HEAD~1 HEAD)
Write-Host "Changed files: $($changed -join ', ')"
$params.ChangedFiles = $changed
$params.OldApiSpec = 'tools\prev-api.json'
}

.\pipeline\Invoke-AiDocsPipeline.ps1 @params

# ── 8. Commit changes to current branch ──────────────────────────────────
- name: Commit changes to current branch
shell: pwsh
run: |
git config user.email "github-actions[bot]@users.noreply.github.com"
git config user.name "github-actions[bot]"

# Stage only the aidocs/ output directory
git add aidocs/
$staged = git diff --cached --stat

if ($staged) {
git commit -m "docs: generate aidocs ${{ steps.version.outputs.version }} [skip ci]"
git push origin HEAD
Write-Host "Changes committed and pushed to current branch." -ForegroundColor Green
} else {
Write-Host "No changes in aidocs/ — nothing to commit." -ForegroundColor DarkGray
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ _site
*.testlog
.vscode
.vs
tools/
_update-prompt.md
.api-spec-hash
173 changes: 173 additions & 0 deletions articles/LCPublicAPI/aidocs/guides/api-clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# API Clients (.NET and Java SDKs)

Both SDKs are auto-generated from the API contracts. Minor version bumps do not guarantee backward compatibility.

## .NET SDK

**Target:** .NET Standard 2.0 ([compatibility matrix](https://docs.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0#net-implementation-support))
**Package:** [Rws.LanguageCloud.Sdk on NuGet](https://www.nuget.org/packages/Rws.LanguageCloud.Sdk)

### Initialization

```csharp
using Rws.LanguageCloud.Sdk;

ServiceCredentials credentials = new ServiceCredentials("CLIENT_ID", "CLIENT_SECRET", "TENANT_ID");
var provider = new LanguageCloudClientProvider("eu"); // default region is "eu"
var projectClient = provider.GetProjectClient(credentials);
```

Three factory methods per client:

| Method | Auth behavior |
|---|---|
| `GetProjectClient(credentials, handlers)` | Implicit auth via `ServiceCredentials` |
| `GetProjectClient(handlers)` | Auth via context scoping (`ApiClientContext.BeginScope`) |
| `GetProjectClientNoAuth(handlers)` | No implicit auth; provide a custom `DelegatingHandler` |

**Important:** Each call to `GetProjectClient` creates a new `HttpClient` instance. Reuse the same instance via Dependency Injection to avoid multiple token caches.

### Token Management

Token caching is automatic. Tokens are reused until expiry. Each application instance holds its own cache; restarts reset the cache.

### Error Handling

All exceptions inherit from `ApiClientException` and expose an `ApiErrorResponse`.

| Exception | Cause |
|---|---|
| `ModelDeserializationException` | Response deserialization failed |
| `ApiUnauthorizedException` | Identity not recognized |
| `ApiPermissionException` | No permission for the resource |
| `ApiForbiddenException` | No access to the resource |
| `ApiErrorException` | General API error |
| `ApiConnectionException` | Connection failure |
| `TaskCanceledException` | Request timeout |

```csharp
catch (ApiErrorException e) when (e.ApiError.ErrorCode == ErrorCodes.MaxSize)
{
string summary = e.ApiError.Message;
foreach (var detail in e.ApiError.Details) { /* ... */ }
}
```

### Basic Project Flow (.NET)

```csharp
// Create project
var project = await projectClient.CreateProjectAsync(new ProjectCreateRequest
{
Name = "My Project",
ProjectTemplate = new ObjectIdRequest { Id = "TEMPLATE_ID" }
});

// Add source file
var sourceFileClient = provider.GetSourceFileClient(credentials);
using (var stream = File.Open("file.txt", FileMode.Open))
{
await sourceFileClient.AddSourceFileAsync(project.Id,
new SourceFileRequest { Name = "file.txt", Role = SourceFileRequestRole.Translatable,
Type = SourceFileRequestType.Native, Language = "en-US" },
new FileParameter(stream, "file.txt", "text/plain"));
}

// Start project
await projectClient.StartProjectAsync(project.Id);

// Get project (with field selection)
var details = await projectClient.GetProjectAsync(project.Id, "status,quote.totalAmount");
```

---

## Java SDK

**Target:** Java 11+
**Based on:** [OpenFeign](https://spring.io/projects/spring-cloud-openfeign)
**Package:** [lc-public-api-sdk on Maven Central](https://search.maven.org/artifact/com.rws.lt.lc.public-api/lc-public-api-sdk)

```xml
<dependency>
<groupId>com.rws.lt.lc.public-api</groupId>
<artifactId>lc-public-api-sdk</artifactId>
<version>LATEST_VERSION</version>
</dependency>
```

Always use the latest version from Maven Central.

### Initialization

```java
ServiceCredentials credentials = new ServiceCredentials("CLIENT_ID", "CLIENT_SECRET", "TENANT_ID");

LanguageCloudClientProvider provider = LanguageCloudClientProvider.builder()
.withRegionCode("eu") // default is "eu"
.withServiceCredentials(credentials)
.build();

ProjectApi projectApi = provider.getProjectClient();
```

### Token Management

- Tokens are cached in a singleton cache shared across all `LanguageCloudClientProvider` instances in the JVM.
- Tokens are evicted `expiry - 1 minute` before they expire.
- No need to recreate `LanguageCloudClientProvider` or its clients — one instance per application is sufficient.

### Context Scoping (Multi-Tenant)

```java
LCContext.executeInScope(
() -> projectApi.listProjects(new ProjectApi.ListProjectsQueryParams()),
serviceCredentials1,
"trace-id-1"
);
```

Allows making API calls on behalf of different tenants using the same client instance.

### Basic Project Flow (Java)

```java
// Create project
ProjectCreateRequest req = new ProjectCreateRequest();
req.setName("My Project");
req.setProjectTemplate(new ObjectIdRequest().id("TEMPLATE_ID"));
req.setLocation("LOCATION_ID");
req.setLanguageDirections(List.of(new LanguageDirectionRequest()
.sourceLanguage(new SourceLanguageRequest("en-US"))
.targetLanguage(new TargetLanguageRequest("fr-FR"))));

Project project = projectApi.createProject(req, new ProjectApi.CreateProjectQueryParams());

// Add source file
SourceFileApi sourceFileApi = provider.getSourceFileClient();
SourceFileRequest fileProps = new SourceFileRequest();
fileProps.setLanguage(new LanguageRequest("en-US"));
fileProps.setName("file.txt");
fileProps.setRole(SourceFileRequest.RoleEnum.TRANSLATABLE);
fileProps.setType(SourceFileRequest.TypeEnum.NATIVE);
sourceFileApi.addSourceFile(project.getId(), fileProps, new File("file.txt"));

// Start project
projectApi.startProject(project.getId());

// Get project
Project details = projectApi.getProject(project.getId(), "status,quote.totalAmount");
```

### v25.x.x Migration (Fat JAR → Light JAR)

Key dependency changes in v25.x.x:

| Old | New |
|---|---|
| JUnit 4 | JUnit 5 (Jupiter) |
| Apache HttpClient 4 (`org.apache.http`) | Apache HttpClient 5 (`org.apache.hc.core5.http`) |
| Commons Lang 2 (`org.apache.commons.lang`) | Commons Lang 3 (`org.apache.commons.lang3`) |
| OpenAPI Generator 6.5.0 | OpenAPI Generator 7.14.0 |

For most users: update the version in `pom.xml` and recompile. Run `mvn dependency:tree` to detect conflicts.
69 changes: 69 additions & 0 deletions articles/LCPublicAPI/aidocs/guides/async-polling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Async Operations and Polling

Several API operations are asynchronous: they accept a request, return an identifier, and complete in the background. The caller must poll a status endpoint until completion, then optionally download the result.

## General Pattern

1. **Trigger** — `POST` or `PUT` to start the operation. Response: `202 Accepted` (or `201 Created`) with an operation ID.
2. **Poll** — `GET` the status endpoint with the operation ID. Repeat until `status` is `done` (or `completed`).
3. **Consume** — `GET` the download endpoint (if applicable).

## Operations Using This Pattern

### Target File Export

| Step | Method | Endpoint |
|---|---|---|
| Trigger | `POST` | `/projects/{projectId}/target-files/{targetFileId}/versions/{versionId}/export` |
| Poll | `GET` | `/projects/{projectId}/target-files/{targetFileId}/versions/{versionId}/export` |
| Download | `GET` | `/projects/{projectId}/target-files/{targetFileId}/versions/{versionId}/export/download` |

The export converts a BCM target file to native or SDLXLIFF format (controlled by the `format` query parameter). Only available on tasks where the output is a bilingual target file. BCM and native format files must be downloaded directly via [Download Target File Version](../../api/Public-API.v1-fv.html#/operations/DownloadFileVersion).

### Target File Import (SDLXLIFF)

| Step | Method | Endpoint |
|---|---|---|
| Trigger | `POST` | `/projects/{projectId}/target-files/{targetFileId}/versions/imports` |
| Poll | `GET` | `/projects/{projectId}/target-files/{targetFileId}/versions/imports/{importId}` |

Returns `importId`. Poll until complete.

### Translation Memory Import

| Step | Method | Endpoint |
|---|---|---|
| Trigger | `POST` | `/translation-memories/{translationMemoryId}/import` |
| Poll | `GET` | `/translation-memories/{translationMemoryId}/import/{importId}` |

Supported input formats: `tmx`, `sdltm`, `zip`, `tmx.gz`, `sdlxliff`. Poll until `status` = `done`.

### Translation Memory Export

| Step | Method | Endpoint |
|---|---|---|
| Trigger | `POST` | `/translation-memories/{translationMemoryId}/export` |
| Poll | `GET` | `/translation-memories/export/{exportId}` |
| Download | `GET` | `/translation-memories/export/{exportId}/download` |

Downloaded file format: `tmx.gz`.

### Quote Report Export

| Step | Method | Endpoint |
|---|---|---|
| Trigger | `POST` | `/projects/{projectId}/quote-report/export` |
| Poll | `GET` | `/projects/{projectId}/quote-report/export` |
| Download | `GET` | `/projects/{projectId}/quote-report/download` |

Format options: `PDF` (default), `Excel`. Language options via `languageId`: `en`, `de`, `fr`, `fr-CA`, `ja`, `es`, `zh-CN`, `nl`, `it`.

## Project Status Tracking

Projects are not polled via a separate endpoint — use `GET /projects/{projectId}` and inspect the `status` field. Use `GET /projects/{projectId}/tasks?fields=taskType,status` to monitor individual task completion. A project is fully translated when all tasks have `status: completed`.

## Polling Recommendations

- Do not poll at a rate that could trigger rate limits (see [rate-limits.md](./rate-limits.md)).
- Use exponential back-off for long-running operations such as large TM imports/exports.
- For file upload/download operations, check applicable [rate limits](./rate-limits.md) (5 req/s, 5,000/day for file endpoints).
Loading
Loading