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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ node_modules/
# Buf cache
.buf/

# OpenAPI Generator CLI version/config (created when running gen.sh)
openapitools.json

# Build artifacts
*.log
*.tmp
Expand Down
58 changes: 39 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,34 @@ Used to store protos and auto-generated clients for public- and external-facing
3. **Big Integers**: Go's `big.Int` fields are represented as `string` fields in protobuf for maximum precision and compatibility.
4. **Timestamps**: Go's `time.Time` is mapped to `google.protobuf.Timestamp`.

## Prerequisites

- **Go** – for protobuf/OpenAPI code generation (see `install-tool.sh`).
- **Node.js** – for Swagger merge, Redoc, and OpenAPI client generation.
- **Java 11+** – required only when generating API clients (TypeScript, Java, Python, C#). The client generator is Java-based.

## Code Generation

Generate clients (for validation purposes):
Generate all code (Go, OpenAPI specs, docs) and API clients for all supported languages:

```bash
make gen
# or directly:
# or:
./gen.sh
./gen.sh all
```

Generate clients for a single language:

```bash
./gen.sh typescript # TypeScript (Fetch API)
./gen.sh java # Java (native HttpClient)
./gen.sh python # Python (urllib3)
./gen.sh csharp # C# (HttpClient, netstandard2.0)
```

Clients are framework-agnostic. Each language is generated for both **egress** (shop APIs) and **ingress** (studio APIs) from their merged OpenAPI specs.

Lint protobuf files:

```bash
Expand All @@ -51,37 +69,39 @@ make format

## Generated Output

To validate correctness of the protos, this repo comes with commands to generate:
Generation produces:

- Go structs and gRPC client/server code
- gRPC-Gateway HTTP/JSON bindings
- OpenAPI v2 (swagger) documentation
- API clients for TypeScript, Java, Python, and C# for both egress and ingress (when requested via `./gen.sh [language]`)

Generated files are placed in the `gen/` directory and are not committed to version control. Instead, each project using these protos should build their own clients as needed.
Generated files are in the `gen/` directory and are not committed. Each consuming project should generate its own clients as needed.

### Generated Output Structure

Running `make gen` from the repo root creates:
Running `make gen` or `./gen.sh all` creates:

```
gen/
├── proto/ # Generated Go code
│ └── server/shop/
│ ├── catalog/v1/ # Catalog service Go structs & gRPC
│ ├── purchase/v1/ # Purchase service Go structs & gRPC
│ └── user/v1/ # User service Go structs & gRPC
├── openapiv2/ # OpenAPI/Swagger documentation
│ └── server/shop/
│ ├── catalog/v1/ # service.swagger.json (rich) + enums.swagger.json (minimal)
│ ├── purchase/v1/ # service.swagger.json (rich) + enums.swagger.json (minimal)
│ └── user/v1/ # service.swagger.json (rich)
└── typescript/ # TypeScript API clients
└── server/shop/
├── catalog/ # catalog-client.ts
├── purchase/ # purchase-client.ts
└── user/ # user-client.ts
│ └── server/...
├── openapiv2/ # Per-service OpenAPI/Swagger specs
│ └── server/...
├── clients-egress/ # Egress API clients (shop: catalog, user, purchase)
│ ├── typescript/
│ ├── java/
│ ├── python/ # package: stash_api
│ └── csharp/
└── clients-ingress/ # Ingress API clients (studio)
├── typescript/
├── java/
├── python/ # package: stash_api_ingress
└── csharp/
```

Merged Swagger and Redoc HTML are written to `docs/gen/`.

### About Swagger Files

- **Service files** (`service.swagger.json`) are rich with API endpoints and full documentation
Expand Down
129 changes: 103 additions & 26 deletions gen.sh
Original file line number Diff line number Diff line change
@@ -1,48 +1,125 @@
#!/bin/bash

# This script generates code from protobuf files using buf.
# It generates Go code, gRPC gateway code, and OpenAPI documentation.
# The generated code is placed in the gen/ directory for use in the project.
# Generates code from protobuf files (Go, OpenAPI) and API clients for TypeScript, Java, Python, C#.
# Clients are generated for both egress (gen/clients-egress/) and ingress (gen/clients-ingress/) specs.
# Usage: ./gen.sh [language]
# language: typescript | java | python | csharp | all (default: all)

set -ex # halt on error + print commands
set -e

# Install necessary tools
./install-tool.sh
usage() {
echo "Usage: ./gen.sh [language]"
echo " language: typescript | java | python | csharp | all (default: all)"
exit 1
}

LANG="${1:-all}"
case "$LANG" in
typescript|java|python|csharp|all) ;;
*) usage ;;
esac

# Add Go bin directory to PATH so protoc plugins can be found
# Go installs binaries to $GOPATH/bin or $HOME/go/bin
# Add Go bin to PATH for protoc plugins
if [ -n "$GOPATH" ]; then
export PATH="$GOPATH/bin:$PATH"
export PATH="$GOPATH/bin:$PATH"
else
export PATH="$HOME/go/bin:$PATH"
export PATH="$HOME/go/bin:$PATH"
fi

# Clean up previous generated files
./install-tool.sh

rm -rf gen/
rm -rf docs/gen/

# Generate Go code, gRPC services, gateway code, and OpenAPI docs
echo "Generating Go code and OpenAPI specs..."
buf generate

# Generate TypeScript clients from OpenAPI specs
echo "Generating TypeScript clients..."
mkdir -p gen/typescript/server/egress/shop/catalog gen/typescript/server/egress/shop/user gen/typescript/server/egress/shop/purchase
npx --yes swagger-typescript-api@9.3.1 -p ./gen/openapiv2/server/egress/shop/catalog/v1/service.swagger.json -o ./gen/typescript/server/egress/shop/catalog/ -n catalog-client.ts --route-types --module-name-index=1 --no-client
npx swagger-typescript-api@9.3.1 -p ./gen/openapiv2/server/egress/shop/user/v1/service.swagger.json -o ./gen/typescript/server/egress/shop/user/ -n user-client.ts --route-types --module-name-index=1 --no-client
npx swagger-typescript-api@9.3.1 -p ./gen/openapiv2/server/egress/shop/purchase/v1/service.swagger.json -o ./gen/typescript/server/egress/shop/purchase/ -n purchase-client.ts --route-types --module-name-index=1 --no-client

# Merge all OpenAPI specs into a single swagger file
echo "Merging Swagger files..."
mkdir -p ./docs/gen/
mkdir -p docs/gen
npx --yes swagger-merger@1.5.4 -i ./docs/config/swagger-merger-config.json -o ./docs/gen/swagger.v1.json
npx --yes swagger-merger@1.5.4 -i ./docs/config/swagger-merger-ingress-config.json -o ./docs/gen/swagger.ingress.v1.json

echo "Building Redoc static HTML file..."
echo "Building Redoc docs..."
npx --yes @redocly/cli@2.6.0 build-docs ./docs/gen/swagger.v1.json -o ./docs/gen/redoc.v1.html
npx --yes @redocly/cli@2.6.0 build-docs ./docs/gen/swagger.ingress.v1.json -o ./docs/gen/redoc.ingress.v1.html

check_java() {
if ! command -v java &>/dev/null; then
echo "Java 11+ is required for client generation. Install Java and try again."
exit 1
fi
}

run_openapi_gen() {
check_java
local spec="$1"
local generator="$2"
local output_dir="$3"
shift 3
npx --yes @openapitools/openapi-generator-cli generate \
-i "$spec" \
-g "$generator" \
-o "$output_dir" \
--skip-validate-spec \
"$@"
}

generate_for_lang() {
local lang="$1"
local egress_out="$2"
local ingress_out="$3"
local py_package_egress="$4"
local py_package_ingress="$5"

echo "Merging Ingress Swagger files..."
npx swagger-merger@1.5.4 -i ./docs/config/swagger-merger-ingress-config.json -o ./docs/gen/swagger.ingress.v1.json
case "$lang" in
typescript)
echo "Generating TypeScript client (egress)..."
mkdir -p "$egress_out"
run_openapi_gen ./docs/gen/swagger.v1.json typescript-fetch "$egress_out"
echo "Generating TypeScript client (ingress)..."
mkdir -p "$ingress_out"
run_openapi_gen ./docs/gen/swagger.ingress.v1.json typescript-fetch "$ingress_out"
;;
java)
echo "Generating Java client (egress)..."
mkdir -p "$egress_out"
run_openapi_gen ./docs/gen/swagger.v1.json java "$egress_out" --additional-properties=library=native
echo "Generating Java client (ingress)..."
mkdir -p "$ingress_out"
run_openapi_gen ./docs/gen/swagger.ingress.v1.json java "$ingress_out" --additional-properties=library=native
;;
python)
echo "Generating Python client (egress)..."
mkdir -p "$egress_out"
run_openapi_gen ./docs/gen/swagger.v1.json python "$egress_out" --additional-properties=packageName="$py_package_egress"
echo "Generating Python client (ingress)..."
mkdir -p "$ingress_out"
run_openapi_gen ./docs/gen/swagger.ingress.v1.json python "$ingress_out" --additional-properties=packageName="$py_package_ingress"
;;
csharp)
echo "Generating C# client (egress)..."
mkdir -p "$egress_out"
run_openapi_gen ./docs/gen/swagger.v1.json csharp "$egress_out" \
--additional-properties=library=httpclient,targetFramework=netstandard2.0
echo "Generating C# client (ingress)..."
mkdir -p "$ingress_out"
run_openapi_gen ./docs/gen/swagger.ingress.v1.json csharp "$ingress_out" \
--additional-properties=library=httpclient,targetFramework=netstandard2.0
;;
esac
}

echo "Building Ingress Redoc static HTML file..."
npx @redocly/cli@2.6.0 build-docs ./docs/gen/swagger.ingress.v1.json -o ./docs/gen/redoc.ingress.v1.html
case "$LANG" in
typescript) generate_for_lang typescript gen/clients-egress/typescript gen/clients-ingress/typescript stash_api stash_api_ingress ;;
java) generate_for_lang java gen/clients-egress/java gen/clients-ingress/java stash_api stash_api_ingress ;;
python) generate_for_lang python gen/clients-egress/python gen/clients-ingress/python stash_api stash_api_ingress ;;
csharp) generate_for_lang csharp gen/clients-egress/csharp gen/clients-ingress/csharp stash_api stash_api_ingress ;;
all)
generate_for_lang typescript gen/clients-egress/typescript gen/clients-ingress/typescript stash_api stash_api_ingress
generate_for_lang java gen/clients-egress/java gen/clients-ingress/java stash_api stash_api_ingress
generate_for_lang python gen/clients-egress/python gen/clients-ingress/python stash_api stash_api_ingress
generate_for_lang csharp gen/clients-egress/csharp gen/clients-ingress/csharp stash_api stash_api_ingress
;;
esac

echo "Code generation completed successfully!"
Loading