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
77 changes: 67 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ Contains shared models used by all service variants:
- **`models/clusters/`** - Cluster resource definitions (interfaces and base models)
- **`models/statuses/`** - Status resource definitions for clusters and nodepools
- **`models/nodepools/`** - NodePool resource definitions
- **`models/compatibility/`** - Compatibility endpoints
- **`models/common/`** - Common models and types (APIResource, Error, QueryParams, etc.)

### `/models-core`
Expand All @@ -98,9 +97,24 @@ Contains GCP provider-specific model definitions:
Contains service definitions that generate the OpenAPI specifications:

- **`services/clusters.tsp`** - Cluster resource endpoints
- **`services/statuses.tsp`** - Status resource endpoints
- **`services/statuses.tsp`** - Status resource endpoints (GET only - public API)
- **`services/statuses-internal.tsp`** - Status write endpoints (POST - internal API, see below)
- **`services/nodepools.tsp`** - NodePool resource endpoints
- **`services/compatibility.tsp`** - Compatibility endpoints

#### Public vs Internal API Split

The status endpoints are split into two files to support different API consumers:

| File | Operations | Audience | Included in Build |
|------|------------|----------|-------------------|
| `statuses.tsp` | GET (read) | External clients | ✅ Yes (default) |
| `statuses-internal.tsp` | POST (write) | Internal adapters | ❌ No (opt-in) |

**Why the split?**
- **External clients** (UI, CLI, monitoring) only need to read status information
- **Internal adapters** (validator, provisioner, dns) need to write/update status reports
- Separating these allows generating different API contracts for different audiences


## Prerequisites

Expand Down Expand Up @@ -137,23 +151,53 @@ This installs all required TypeSpec libraries to the local `node_modules/` direc

The repository uses a single `main.tsp` entry point. To generate either the core API or GCP API, you need to re-link the `aliases.tsp` file to point to the desired provider aliases file.

### Using the Build Script (Recommended)
### Output Formats

The build system supports two OpenAPI formats:
- **OpenAPI 3.0** (default) - Modern format with full feature support
- **OpenAPI 2.0 (Swagger)** - Legacy format for compatibility with older tools

The easiest way to build the OpenAPI schema is using the provided `build-schema.sh` script:
### Using npm Scripts (Recommended)

The easiest way to build the OpenAPI schema is using the provided npm scripts:

```bash
# Build Core API
./build-schema.sh core
# Build OpenAPI 3.0 only
npm run build:core # Build Core API (OpenAPI 3.0)
npm run build:gcp # Build GCP API (OpenAPI 3.0)

# Build both OpenAPI 3.0 and OpenAPI 2.0 (Swagger)
npm run build:core:swagger # Build Core API with Swagger
npm run build:gcp:swagger # Build GCP API with Swagger

# Build all providers with both formats
npm run build:all
```

# Build GCP API
### Using the Build Script Directly

You can also use the `build-schema.sh` script directly:

```bash
# Build OpenAPI 3.0 only
./build-schema.sh core
./build-schema.sh gcp

# Build with OpenAPI 2.0 (Swagger) output
./build-schema.sh core --swagger
./build-schema.sh gcp --swagger
# or use the alias:
./build-schema.sh gcp --openapi2
```

The script automatically:
1. Validates the provider parameter
2. Re-links `aliases.tsp` to the appropriate provider aliases file
3. Compiles the TypeSpec to generate the OpenAPI schema
4. Outputs the result to `schemas/{provider}/openapi.yaml` (e.g., `schemas/core/openapi.yaml` or `schemas/gcp/openapi.yaml`)
3. Compiles the TypeSpec to generate the OpenAPI 3.0 schema
4. (If `--swagger` flag is used) Converts OpenAPI 3.0 to OpenAPI 2.0 (Swagger)
5. Outputs the results to `schemas/{provider}/`:
- `openapi.yaml` - OpenAPI 3.0 specification
- `swagger.yaml` - OpenAPI 2.0 (Swagger) specification (if `--swagger` flag is used)

**Extending to new providers**: Simply create `aliases-{provider}.tsp` and the script will automatically detect and support it.

Expand All @@ -174,6 +218,12 @@ If you prefer to build manually:

Output: `tsp-output/schema/openapi.yaml`

3. (Optional) Convert to OpenAPI 2.0 (Swagger):
```bash
npx api-spec-converter --from=openapi_3 --to=swagger_2 --syntax=yaml \
tsp-output/schema/openapi.yaml > tsp-output/schema/swagger.yaml
```

#### Build GCP API
1. Re-link `aliases.tsp` to `aliases-gcp.tsp`:
```bash
Expand All @@ -187,6 +237,12 @@ If you prefer to build manually:

Output: `tsp-output/schema/openapi.yaml`

3. (Optional) Convert to OpenAPI 2.0 (Swagger):
```bash
npx api-spec-converter --from=openapi_3 --to=swagger_2 --syntax=yaml \
tsp-output/schema/openapi.yaml > tsp-output/schema/swagger.yaml
```

**Note**: The `aliases.tsp` file controls which provider-specific `ClusterSpec` definition is used throughout the service definitions. By re-linking it to either `aliases-core.tsp` or `aliases-gcp.tsp`, you switch between the generic `Record<unknown>` spec and the GCP-specific `GCPClusterSpec`.

## Architecture
Expand Down Expand Up @@ -249,6 +305,7 @@ To add a new service (e.g., with additional endpoints):
- `@typespec/http` - HTTP protocol support
- `@typespec/openapi` - OpenAPI decorators
- `@typespec/openapi3` - OpenAPI 3.0 emitter
- `api-spec-converter` - Converts OpenAPI 3.0 to OpenAPI 2.0 (Swagger)


## Developing with the Visual Studio Typespec extension
Expand Down
3 changes: 2 additions & 1 deletion aliases-core.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import "./models-core/cluster/example_cluster.tsp";
import "./models-core/cluster/example_post.tsp";
import "./models-core/nodepool/model.tsp";
import "./models-core/nodepool/example_nodepool.tsp";
import "./models-core/nodepool/example_post.tsp";
import "./models-core/nodepool/example_post.tsp";
import "./services/statuses-internal.tsp";
91 changes: 79 additions & 12 deletions build-schema.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/bin/bash

# Build HyperFleet OpenAPI Schema
# Usage: ./build-schema.sh [provider]
# provider: core, gcp, or any provider with aliases-{provider}.tsp file
# Usage: ./build-schema.sh <provider> [--swagger|--openapi2]
# provider: core, gcp, or any provider with aliases-{provider}.tsp file (required, must be first argument)
# --swagger, --openapi2: Also generate OpenAPI 2.0 (Swagger) format

set -e

Expand All @@ -12,21 +13,51 @@ GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Get the provider from command line argument
PROVIDER="${1:-core}"
# Parse arguments
GENERATE_SWAGGER=false

# Script directory (where the script is located)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Validate provider argument
if [ -z "$PROVIDER" ]; then
# Provider is required and must be the first argument
if [ $# -lt 1 ]; then
echo -e "${RED}Error: Provider argument is required${NC}"
echo "Usage: $0 [provider]"
echo "Usage: $0 <provider> [--swagger|--openapi2]"
echo " provider: core, gcp, or any provider with aliases-{provider}.tsp file"
echo " --swagger, --openapi2: Also generate OpenAPI 2.0 (Swagger) format"
exit 1
fi

PROVIDER="$1"
shift

# Check if provider looks like an option (starts with -)
if [[ "$PROVIDER" == -* ]]; then
echo -e "${RED}Error: Provider must be the first argument${NC}"
echo "Usage: $0 <provider> [--swagger|--openapi2]"
exit 1
fi

# Parse remaining options
for arg in "$@"; do
case $arg in
--swagger|--openapi2)
GENERATE_SWAGGER=true
;;
-*)
echo -e "${RED}Error: Unknown option: $arg${NC}"
echo "Usage: $0 <provider> [--swagger|--openapi2]"
exit 1
;;
*)
echo -e "${RED}Error: Unexpected argument: $arg${NC}"
echo "Usage: $0 <provider> [--swagger|--openapi2]"
exit 1
;;
esac
done

# Script directory (where the script is located)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# Define the aliases file for the provider
ALIASES_FILE="aliases-${PROVIDER}.tsp"

Expand Down Expand Up @@ -57,7 +88,21 @@ if ! command -v tsp &> /dev/null; then
exit 1
fi

# Check if api-spec-converter is available when swagger output is requested
if [ "$GENERATE_SWAGGER" = true ]; then
if ! npx api-spec-converter --version &> /dev/null; then
echo -e "${RED}Error: api-spec-converter not found. Please install it.${NC}"
echo "Install with: npm install --save-dev api-spec-converter"
exit 1
fi
fi

echo -e "${GREEN}Building HyperFleet API schema for provider: ${PROVIDER}${NC}"
if [ "$GENERATE_SWAGGER" = true ]; then
echo -e "${GREEN}Output formats: OpenAPI 3.0 + OpenAPI 2.0 (Swagger)${NC}"
else
echo -e "${GREEN}Output format: OpenAPI 3.0${NC}"
fi
echo ""

# Step 1: Re-link aliases.tsp to the provider-specific aliases file
Expand Down Expand Up @@ -93,7 +138,7 @@ if tsp compile main.tsp --output-dir "$TEMP_OUTPUT_DIR"; then
if [ -f "${TEMP_OUTPUT_DIR}/schema/openapi.yaml" ]; then
mv "${TEMP_OUTPUT_DIR}/schema/openapi.yaml" "${OUTPUT_DIR}/openapi.yaml"
echo ""
echo -e "${GREEN}✓ Successfully generated OpenAPI schema${NC}"
echo -e "${GREEN}✓ Successfully generated OpenAPI 3.0 schema${NC}"
echo -e "${GREEN}Output: ${OUTPUT_DIR}/openapi.yaml${NC}"
else
echo ""
Expand All @@ -107,3 +152,25 @@ else
exit 1
fi

# Step 4: Convert to OpenAPI 2.0 (Swagger) if requested
if [ "$GENERATE_SWAGGER" = true ]; then
echo ""
echo -e "${YELLOW}Step 4: Converting to OpenAPI 2.0 (Swagger)...${NC}"

if npx api-spec-converter \
--from=openapi_3 \
--to=swagger_2 \
--syntax=yaml \
"${OUTPUT_DIR}/openapi.yaml" > "${OUTPUT_DIR}/swagger.yaml" 2>/dev/null; then
echo -e "${GREEN}✓ Successfully generated OpenAPI 2.0 (Swagger) schema${NC}"
echo -e "${GREEN}Output: ${OUTPUT_DIR}/swagger.yaml${NC}"
else
echo -e "${RED}✗ Failed to convert to OpenAPI 2.0 (Swagger)${NC}"
echo "The OpenAPI 3.0 schema may contain features not supported in OpenAPI 2.0"
rm -f "${OUTPUT_DIR}/swagger.yaml"
exit 1
fi
fi

echo ""
echo -e "${GREEN}Build complete!${NC}"
8 changes: 5 additions & 3 deletions main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import "@typespec/openapi3";
import "./services/clusters.tsp";
import "./services/statuses.tsp";
import "./services/nodepools.tsp";
import "./services/compatibility.tsp";
// Provider-specific security is imported via aliases.tsp
import "./aliases.tsp";

using Http;
using OpenAPI;

Expand All @@ -18,8 +20,8 @@ using OpenAPI;
*
*/
@service(#{ title: "HyperFleet API" })
@info(#{ version: "1.0.0", contact: #{ name: "HyperFleet Team" } })
@server("http://localhost:8000", "Development")
@info(#{ version: "1.0.0", contact: #{ name: "HyperFleet Team" }, license: #{ name: "Apache 2.0" ,url: "https://www.apache.org/licenses/LICENSE-2.0"} })
@server("https://hyperfleet.redhat.com", "Production")
@route("/api/hyperfleet/v1")
namespace HyperFleet;

Expand Down
Loading