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
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This repository supports the development of the Hyperfleet OpenAPI contract, but is not the source-of-truth for the OpenAPI contract.

This project hosts the TypeSpec files to generate the HyperFleet core OpenAPI specification. TypeSpec is an implementation detail providing better ergonomics than writing contracts in plain YAML. The repository generates the core provider contract; the GCP-specific contract lives in [hyperfleet-api-spec-gcp](https://github.com/openshift-hyperfleet/hyperfleet-api-spec-gcp).
This project hosts the TypeSpec files to generate the HyperFleet core OpenAPI specification. TypeSpec is an implementation detail providing better ergonomics than writing contracts in plain YAML. The repository generates the core provider contract; the provider-specific contract lives in [hyperfleet-api-spec-template](https://github.com/openshift-hyperfleet/hyperfleet-api-spec-template).

Access to the OpenAPI contract source of truth in hyperfleet-api repository:

Expand Down Expand Up @@ -68,7 +68,7 @@ The repository is organized with root-level configuration files and two main dir

### `/shared`

Contains models and services shared across providers (also published as an npm package for consumption by provider-specific repos like `hyperfleet-api-spec-gcp`):
Contains models and services shared across providers (also published as an npm package for consumption by provider-specific repos like `hyperfleet-api-spec-template`):

- **`shared/models/clusters/`** - Cluster resource definitions (interfaces and base models)
- **`shared/models/statuses/`** - Status resource definitions for clusters and nodepools
Expand All @@ -89,10 +89,10 @@ Contains core-specific models and internal services:

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

| File | Operations | Audience | Included in Build |
|------|------------|----------|-------------------|
| `shared/services/statuses.tsp` | GET (read) | External clients | ✅ Yes (default) |
| `core/services/statuses-internal.tsp` | PUT (write) | Internal adapters | ❌ No (opt-in) |
| File | Operations | Audience | Included in Build |
| ------------------------------------- | ----------- | ----------------- | ----------------- |
| `shared/services/statuses.tsp` | GET (read) | External clients | ✅ Yes (default) |
| `core/services/statuses-internal.tsp` | PUT (write) | Internal adapters | ❌ No (opt-in) |
Comment thread
coderabbitai[bot] marked this conversation as resolved.

**Why the split?**

Expand Down Expand Up @@ -149,7 +149,7 @@ The HyperFleet API provides simple CRUD operations for managing cluster resource

## Adding a New Provider

Provider-specific contracts live in their own repository and consume this repo as an npm package (the `hyperfleet` package). See [hyperfleet-api-spec-gcp](https://github.com/openshift-hyperfleet/hyperfleet-api-spec-gcp) for a reference implementation.
Provider-specific contracts live in their own repository and consume this repo as an npm package (the `hyperfleet` package). See [hyperfleet-api-spec-template](https://github.com/openshift-hyperfleet/hyperfleet-api-spec-template) for a reference implementation.

## Adding a New Service

Expand All @@ -162,7 +162,7 @@ To add a new service (e.g., with additional endpoints):
import "@typespec/openapi";
import "../models/common/model.tsp";
// ... other imports as needed

namespace HyperFleet;
@route("/new-resource")
interface NewService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import "@typespec/http";
import "@typespec/openapi";
import "@typespec/openapi3";

import "../models/resource/model.tsp";
import "../models/common/model.tsp";
import "../models/statuses/model.tsp";
import "../../shared/models/resource/model.tsp";
import "../../shared/models/common/model.tsp";
import "../../shared/models/statuses/model.tsp";

using Http;
using OpenAPI;
Expand Down Expand Up @@ -90,4 +90,55 @@ interface Resources {
| Error
| BadRequestResponse;

/**
* Permanently removes the resource record from the database for a resource stuck in Finalizing state.
* This is a database-only operation. Requires a reason for audit purposes.
*/
@route("/{resource_id}/force-delete")
@post
@summary("Force-delete a resource")
@operationId("forceDeleteResource")
forceDeleteResource(
@path resource_id: string,
@body body: ForceDeleteRequest,
): {
@statusCode statusCode: 204;
} | Error
| NotFoundResponse
| BadRequestResponse
| ConflictResponse;
}

@tag("Resource statuses")
@route("/resources/{resource_id}/statuses")
@useAuth(HyperFleet.BearerAuth)
interface ResourceStatuses {
/**
* Returns adapter statuses for a resource.
*/
@route("")
@get
@summary("List resource statuses")
@operationId("getResourceStatuses")
getResourceStatuses(
@path resource_id: string,
...QueryParams,
): Body<AdapterStatusList>
| Error
| NotFoundResponse
| BadRequestResponse;

@route("")
@put
@summary("Adapter creates or updates resource status")
@operationId("putResourceStatuses")
@doc("Adapters call this endpoint to report status for a resource after each evaluation. The adapter's status entry is created if it doesn't exist, or updated if it does (upserted by adapter name).")
putResourceStatuses(
@path resource_id: string,
@body body: AdapterStatusCreateRequest,
):
| (CreatedResponse & AdapterStatus)
| BadRequestResponse
| NotFoundResponse
| ConflictResponse;
}
83 changes: 39 additions & 44 deletions core/services/statuses-internal.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,25 @@ namespace HyperFleet;
@route("/clusters/{cluster_id}/statuses")
@useAuth(HyperFleet.BearerAuth)
@tag("Cluster statuses")
interface ClusterStatusesInternal {
interface ClusterStatuses {
/**
* Returns adapter status reports for this cluster
*/
@route("")
@get
@summary("List all adapter statuses for cluster")
@operationId("getClusterStatuses")
getClusterStatuses(
/**
* Cluster ID
*/
@path cluster_id: string,
...QueryParams
): Body<AdapterStatusList>
| Error
| NotFoundResponse
| BadRequestResponse;
Comment thread
rh-amarin marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

@route("")
@put
@summary("Adapter creates or updates cluster status")
Expand All @@ -37,7 +55,26 @@ interface ClusterStatusesInternal {
@tag("NodePool statuses")
@route("/clusters/{cluster_id}/nodepools/{nodepool_id}/statuses")
@useAuth(HyperFleet.BearerAuth)
interface NodePoolStatusesInternal {
interface NodePoolStatuses {
/**
* Returns adapter status reports for this nodepool
*/
@route("")
@get
@summary("List all adapter statuses for nodepools")
@operationId("getNodePoolsStatuses")
getNodePoolsStatuses(
/**
* Cluster ID
*/
@path cluster_id: string,
@path nodepool_id: string,
...QueryParams
): Body<AdapterStatusList>
| Error
| NotFoundResponse
| BadRequestResponse;

@route("")
@put
@summary("Adapter creates or updates nodepool status")
Expand All @@ -61,45 +98,3 @@ interface NodePoolStatusesInternal {
| NotFoundResponse
| ConflictResponse;
}

@tag("Resource statuses")
@route("/resources/{resource_id}/statuses")
@useAuth(HyperFleet.BearerAuth)
interface ResourceStatusesInternal {
@route("")
@put
@summary("Adapter creates or updates resource status")
@operationId("putResourceStatuses")
@doc("Adapters call this endpoint to report status for a resource after each evaluation. The adapter's status entry is created if it doesn't exist, or updated if it does (upserted by adapter name).")
putResourceStatuses(
@path resource_id: string,
@body body: AdapterStatusCreateRequest,
):
| (CreatedResponse & AdapterStatus)
| BadRequestResponse
| NotFoundResponse
| ConflictResponse;
}

@tag("Resources")
@route("/resources")
@useAuth(HyperFleet.BearerAuth)
interface ResourcesForceDelete {
/**
* Permanently removes the resource record from the database for a resource stuck in Finalizing state.
* This is a database-only operation. Requires a reason for audit purposes.
*/
@route("/{resource_id}/force-delete")
@post
@summary("Force-delete a resource")
@operationId("forceDeleteResource")
forceDeleteResource(
@path resource_id: string,
@body body: ForceDeleteRequest,
): {
@statusCode statusCode: 204;
} | Error
| NotFoundResponse
| BadRequestResponse
| ConflictResponse;
}
5 changes: 2 additions & 3 deletions main.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import "./core/models/nodepool/example_nodepool.tsp";
import "./core/models/nodepool/example_post.tsp";
import "./core/models/nodepool/example_patch.tsp";
import "./core/services/statuses-internal.tsp";
import "./core/services/resources-internal.tsp";

import "./shared/services/clusters.tsp";
import "./shared/services/statuses.tsp";
import "./shared/services/nodepools.tsp";
import "./shared/services/resources.tsp";
import "./core/services/force-delete-internal.tsp";

using Http;
Expand All @@ -31,7 +30,7 @@ using OpenAPI;
*/
@service(#{ title: "HyperFleet API" })
@info(#{
version: "1.0.18",
version: "1.0.19",
Comment thread
rh-amarin marked this conversation as resolved.
contact: #{
name: "HyperFleet Team",
url: "https://github.com/openshift-hyperfleet",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hyperfleet",
"version": "1.0.18",
"version": "1.0.19",
"type": "module",
"exports": {
"./*": "./*"
Expand Down
Loading