Skip to content
Merged
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 8 additions & 0 deletions .github/workflows/ci_general.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,13 @@ jobs:
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.x"
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: python -m pip install pyyaml
- run: python -m unittest discover tests
- run: ./generate.sh --verify
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
.vscode/**
!.vscode/extensions.json
**.iml

# Temporary OpenAPI Generator output
build/generated/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Rename Portal user management contracts from `Portal User Management` to `User Management`.
- Refocus the repository on the OpenAPI spec contract and stop tracking generated Java, Python, TypeScript, Rust, and Spring Boot source trees.
- Change `generate.sh` to produce temporary Apollo Portal Spring interface smoke output under `build/generated/portal` for local and CI verification.

### Fixed

Expand All @@ -18,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add Consumer Token support notes to user management contracts, including `ManageUsers`-guarded user lookup and mutation operations.
- Add a generation workflow test to ensure generated source trees remain untracked.

## [0.3.5] - 2026-05-31

Expand Down
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# apollo-openapi

![OpenAPI](https://img.shields.io/badge/spec-OpenAPI%203.0.1-blue)
[![npm](https://img.shields.io/npm/v/apollo-openapi?label=npm&color=cb3837&logo=npm)](https://www.npmjs.com/package/apollo-openapi)
[![PyPI version](https://img.shields.io/pypi/v/apollo-openapi.svg)](https://pypi.org/project/apollo-openapi)
[![Rust version](https://img.shields.io/crates/v/apollo-openapi.svg)](https://crates.io/crates/apollo-openapi)

This repository maintains the Apollo OpenAPI contract. The source of truth is
[`apollo-openapi.yaml`](apollo-openapi.yaml).

Generated code is treated as a temporary verification artifact, not as
maintained source code or an official Apollo SDK. Apollo Portal pins a released
`apollo-openapi.yaml` tag and generates its Spring OpenAPI interfaces during
the Portal build.

To verify the spec can still generate the Portal OpenAPI surface:

```bash
./generate.sh --verify
```

The generated files are written under `build/generated/` and are intentionally
ignored by git.
14 changes: 3 additions & 11 deletions clean.sh
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
#!/bin/bash
set -e
set -euo pipefail

JAVA_DIR="java"
PYTHON_DIR="python"
RUST_DIR="rust"
TS_DIR="typescript"

echo "🧹 Cleaning old generated SDKs..."
rm -rf "$JAVA_DIR"
rm -rf "$PYTHON_DIR"
rm -rf "$RUST_DIR"
rm -rf "$TS_DIR"
echo "Cleaning temporary generated artifacts..."
rm -rf build/generated/
298 changes: 69 additions & 229 deletions generate.sh
Original file line number Diff line number Diff line change
@@ -1,247 +1,87 @@
#!/bin/bash
set -e

SPEC_FILE="apollo-openapi.yaml"
JAVA_CLIENT_DIR="java-client"
SPRING_BOOT2_DIR="spring-boot2"
PYTHON_DIR="python"
RUST_DIR="rust"
TS_DIR="typescript"
PYTHON_TEMPLATE_DIR="templates/python"
OPENAPI_GENERATOR_VERSION="6.6.0"

if command -v openapi-generator >/dev/null 2>&1; then
OPENAPI_GENERATOR=(openapi-generator)
set -euo pipefail

SPEC_FILE="${SPEC_FILE:-apollo-openapi.yaml}"
GENERATED_ROOT="build/generated"
PORTAL_OUTPUT_DIR="$GENERATED_ROOT/portal"
OPENAPI_GENERATOR_VERSION="7.20.0"

usage() {
cat <<'EOF'
Usage: ./generate.sh [--verify]

Generates the Apollo Portal OpenAPI Spring interface smoke output under
build/generated/portal. Generated code is a temporary verification artifact and
is not tracked by this repository.

Options:
--verify Generate and assert the expected Portal compatibility surface.
--help Show this help.
EOF
}

VERIFY=false
for arg in "$@"; do
case "$arg" in
--verify)
VERIFY=true
;;
--help|-h)
usage
exit 0
;;
*)
echo "Unknown argument: $arg" >&2
usage >&2
exit 2
;;
esac
done

if command -v npx >/dev/null 2>&1; then
OPENAPI_GENERATOR=(npx --yes @openapitools/openapi-generator-cli)
elif command -v openapi-generator-cli >/dev/null 2>&1; then
OPENAPI_GENERATOR=(openapi-generator-cli)
elif command -v npx >/dev/null 2>&1; then
OPENAPI_GENERATOR=(npx @openapitools/openapi-generator-cli)
elif command -v openapi-generator >/dev/null 2>&1; then
OPENAPI_GENERATOR=(openapi-generator)
else
echo "openapi-generator is required. Install openapi-generator or make npx available."
echo "openapi-generator, openapi-generator-cli, or npx is required." >&2
exit 127
fi

export OPENAPI_GENERATOR_VERSION

JAVA_VERSION_LINE="$(java -version 2>&1 | head -n 1)"
JAVA_VERSION_LINE="$(java -version 2>&1 | head -n 1 || true)"
if [[ "$JAVA_VERSION_LINE" =~ \"([0-9]+) ]] && [ "${BASH_REMATCH[1]}" -ge 9 ]; then
export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS:+$JAVA_TOOL_OPTIONS }--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED"
fi

echo "🧹 Cleaning old generated SDKs..."
rm -rf "$JAVA_CLIENT_DIR"
rm -rf "$SPRING_BOOT2_DIR"
rm -rf "$PYTHON_DIR"
rm -rf "$RUST_DIR"
rm -rf "$TS_DIR"

echo "🚀 Generating Python SDK..."
"${OPENAPI_GENERATOR[@]}" generate \
-i "$SPEC_FILE" \
-g python \
-o "$PYTHON_DIR" \
-t "$PYTHON_TEMPLATE_DIR" \
--package-name apollo_openapi \
--additional-properties=projectName=apollo-openapi,packageVersion=0.3.8

echo "🚀 Generating TypeScript SDK..."
"${OPENAPI_GENERATOR[@]}" generate \
-i "$SPEC_FILE" \
-g typescript-fetch \
-o "$TS_DIR" \
--additional-properties=npmName=apollo-openapi,npmVersion=0.3.8,typescriptThreePlus=true

echo "🚀 Generating Java Client SDK..."
"${OPENAPI_GENERATOR[@]}" generate \
-i "$SPEC_FILE" \
-g java \
-o "$JAVA_CLIENT_DIR" \
--additional-properties hideGenerationTimestamp=true \
--additional-properties=groupId=com.apollo,artifactId=apollo-openapi-client,artifactVersion=0.3.8,packageName=com.apollo.openapi.client

echo "🔧 Preserving Java client operator overloads..."
python3 - <<'PY'
from pathlib import Path

api_file = Path("java-client/src/main/java/org/openapitools/client/api/UserManagementApi.java")
content = api_file.read_text(encoding="utf-8")


def add_overload_before(marker, overload):
global content
if overload in content:
return
if marker not in content:
raise SystemExit(f"Could not find Java client overload marker:\n{marker}")
content = content.replace(marker, overload + marker, 1)


def add_overload_after(marker, overload):
global content
if overload in content:
return
if marker not in content:
raise SystemExit(f"Could not find Java client overload marker:\n{marker}")
content = content.replace(marker, marker + overload, 1)

echo "Cleaning generated Portal smoke output..."
rm -rf "$PORTAL_OUTPUT_DIR"

add_overload_before(
""" @SuppressWarnings("rawtypes")
private okhttp3.Call changeUserEnabledValidateBeforeCall""",
""" /**
* Build call for changeUserEnabled.
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public okhttp3.Call changeUserEnabledCall(OpenUserDTO openUserDTO, final ApiCallback _callback) throws ApiException {
return changeUserEnabledCall(openUserDTO, null, _callback);
}

""",
)
add_overload_after(
""" public void changeUserEnabled(OpenUserDTO openUserDTO, String operator) throws ApiException {
changeUserEnabledWithHttpInfo(openUserDTO, operator);
}

""",
""" /**
* 修改用户启用状态(new added)
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public void changeUserEnabled(OpenUserDTO openUserDTO) throws ApiException {
changeUserEnabled(openUserDTO, null);
}

""",
)
add_overload_before(
""" /**
* 修改用户启用状态(new added) (asynchronously)
* PUT /openapi/v1/users/enabled""",
""" /**
* 修改用户启用状态(new added)
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public ApiResponse<Void> changeUserEnabledWithHttpInfo(OpenUserDTO openUserDTO) throws ApiException {
return changeUserEnabledWithHttpInfo(openUserDTO, null);
}

""",
)
add_overload_before(
""" /**
* Build call for createOrUpdateUser
* @param openUserDTO""",
"""
/**
* 修改用户启用状态(new added) (asynchronously)
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public okhttp3.Call changeUserEnabledAsync(OpenUserDTO openUserDTO, final ApiCallback<Void> _callback) throws ApiException {
return changeUserEnabledAsync(openUserDTO, null, _callback);
}

""",
)
add_overload_before(
""" @SuppressWarnings("rawtypes")
private okhttp3.Call createOrUpdateUserValidateBeforeCall""",
""" /**
* Build call for createOrUpdateUser.
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public okhttp3.Call createOrUpdateUserCall(OpenUserDTO openUserDTO, Boolean isCreate, final ApiCallback _callback) throws ApiException {
return createOrUpdateUserCall(openUserDTO, isCreate, null, _callback);
}

""",
)
add_overload_after(
""" public void createOrUpdateUser(OpenUserDTO openUserDTO, Boolean isCreate, String operator) throws ApiException {
createOrUpdateUserWithHttpInfo(openUserDTO, isCreate, operator);
}

""",
""" /**
* 创建或更新用户(new added)
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public void createOrUpdateUser(OpenUserDTO openUserDTO, Boolean isCreate) throws ApiException {
createOrUpdateUser(openUserDTO, isCreate, null);
}

""",
)
add_overload_before(
""" /**
* 创建或更新用户(new added) (asynchronously)
* POST /openapi/v1/users""",
""" /**
* 创建或更新用户(new added)
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public ApiResponse<Void> createOrUpdateUserWithHttpInfo(OpenUserDTO openUserDTO, Boolean isCreate) throws ApiException {
return createOrUpdateUserWithHttpInfo(openUserDTO, isCreate, null);
}

""",
)
add_overload_before(
""" /**
* Build call for getCurrentUser
* @param _callback""",
"""
/**
* 创建或更新用户(new added) (asynchronously)
* This overload preserves the Java client API for callers that do not need the optional operator query parameter.
*/
public okhttp3.Call createOrUpdateUserAsync(OpenUserDTO openUserDTO, Boolean isCreate, final ApiCallback<Void> _callback) throws ApiException {
return createOrUpdateUserAsync(openUserDTO, isCreate, null, _callback);
}
""",
)

api_file.write_text(content, encoding="utf-8")
PY

echo "🚀 Generating Spring Boot 2 Server..."
echo "Generating Apollo Portal Spring OpenAPI interfaces..."
"${OPENAPI_GENERATOR[@]}" generate \
-i "$SPEC_FILE" \
-g spring \
-o "$SPRING_BOOT2_DIR" \
--additional-properties hideGenerationTimestamp=true \
--additional-properties=groupId=com.apollo,artifactId=apollo-openapi-server,artifactVersion=0.3.8,packageName=com.apollo.openapi.server,basePackage=com.apollo.openapi.server,configPackage=com.apollo.openapi.server.config,modelPackage=com.apollo.openapi.server.model,apiPackage=com.apollo.openapi.server.api,library=spring-boot,java8=true,interfaceOnly=false,delegatePattern=true,useTags=true

echo "📦 Adding Maven Wrapper to Spring Boot 2 project..."
cd "$SPRING_BOOT2_DIR"
mvn -N io.takari:maven:wrapper -Dmaven=3.8.6

cd ..

echo "💡 Spring Boot 2 project ready! To start the server, run:"
echo " cd $SPRING_BOOT2_DIR && ./mvnw spring-boot:run"

if [ "$1" = "--start-spring-boot" ]; then
echo "🚀 Starting Spring Boot server..."
cd "$SPRING_BOOT2_DIR"
./mvnw spring-boot:run &
echo "✅ Spring Boot server started in background. Access it at http://localhost:8080"
cd ..
-o "$PORTAL_OUTPUT_DIR" \
--additional-properties=apiPackage=com.ctrip.framework.apollo.openapi.api,modelPackage=com.ctrip.framework.apollo.openapi.model,invokerPackage=com.ctrip.framework.apollo.openapi.invoker,interfaceOnly=true,useTags=true,dateLibrary=java8,useSpringBoot4=true \
--skip-validate-spec

if [ "$VERIFY" = true ]; then
API_DIR="$PORTAL_OUTPUT_DIR/src/main/java/com/ctrip/framework/apollo/openapi/api"
MODEL_DIR="$PORTAL_OUTPUT_DIR/src/main/java/com/ctrip/framework/apollo/openapi/model"

test -f "$API_DIR/UserManagementApi.java"
test ! -f "$API_DIR/PortalUserManagementApi.java"

for model_name in \
OpenConsumerCreateRequestDTO \
OpenConsumerInfoDTO \
OpenConsumerSummaryDTO \
OpenConsumerTokenDTO
do
test ! -f "$MODEL_DIR/$model_name.java"
done

echo "Portal generation verification passed."
fi

echo "🚀 Generating Rust SDK..."
"${OPENAPI_GENERATOR[@]}" generate \
-i "$SPEC_FILE" \
-g rust \
-o "$RUST_DIR" \
--global-property models,supportingFiles \
--additional-properties=packageName=apollo-openapi,packageVersion=0.3.8

echo "✅ SDK generation complete."

# this is for normalizing generated files and reducing version-only churn.
echo "Cleaning files (trailing spaces, CRLF, EOF newlines, generated version comments)..."
find . -type d \( -name .git -o -name .idea -o -name .mvn -o -name target -o -name build -o -name node_modules \) -prune -o \
-type f \( -name "*.java" -o -name "*.xml" -o -name "*.properties" -o -name "*.md" -o -name "*.yml" -o -name "*.yaml" -o -name "*.gradle" -o -name "*.sh" -o -name "*.py" -o -name "*.pyi" -o -name "*.ts" -o -name "*.rs" -o -name ".editorconfig" -o -name ".gitignore" -o -name ".npmignore" -o -name "mvnw" -o -name "VERSION" \) -print0 \
| xargs -0 perl -i -0777 -pe 's/[ \t]+(?=\r?$)//mg; s/\r//g; s/^[ \t]*\* The version of the OpenAPI document: [^\r\n]*\R//mg; s/^[ \t]*The version of the OpenAPI document: [^\r\n]*\R//mg; s/^body \| Unset \| body was not defined \|$/body | Unset | body was not defined | N\/A/mg; s/\s*\z/\n/s'
echo "Cleaning files (trailing spaces, CRLF, EOF newlines, generated version comments)... Done!"
Loading
Loading