Skip to content
Open
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
51 changes: 39 additions & 12 deletions .github/workflows/regenerate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,83 @@ name: Regenerate Client

on:
workflow_dispatch:
inputs:
openapi_spec_ref:
description: "Git ref in www.hotdata.dev for api/openapi.yaml (same file as sdk-python regenerate)."
required: false
default: main

jobs:
regenerate:
runs-on: ubuntu-latest
env:
# Same OpenAPI document as https://github.com/hotdata-dev/sdk-python/blob/main/.github/workflows/regenerate.yml
OPENAPI_SPEC_OWNER: hotdata-dev
OPENAPI_SPEC_REPO: www.hotdata.dev
OPENAPI_SPEC_PATH: api/openapi.yaml
OPENAPI_SPEC_REF: ${{ inputs.openapi_spec_ref || 'main' }}
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3
with:
app-id: 3060111
private-key: ${{ secrets.HOTDATA_AUTOMATION_PRIVATE_KEY }}
owner: hotdata-dev

- uses: actions/checkout@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ steps.app-token.outputs.token }}

- name: Fetch merged OpenAPI spec
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
with:
distribution: temurin
java-version: "17"

- name: Crate version for generator
id: crate
run: |
v=$(awk -F '"' '/^version = / { print $2; exit }' Cargo.toml)
echo "version=$v" >> "$GITHUB_OUTPUT"

- name: Fetch OpenAPI spec (same source as sdk-python)
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
spec_url="https://api.github.com/repos/${OPENAPI_SPEC_OWNER}/${OPENAPI_SPEC_REPO}/contents/${OPENAPI_SPEC_PATH}?ref=${OPENAPI_SPEC_REF}"
curl -sS -f -L \
-H "Accept: application/vnd.github.v3.raw" \
-H "Authorization: Bearer $GH_TOKEN" \
https://api.github.com/repos/hotdata-dev/www.hotdata.dev/contents/api/openapi.yaml \
"$spec_url" \
-o openapi.yaml

- name: Clean existing source
run: rm -rf src/

- name: Generate client
env:
PACKAGE_VERSION: "0.1.0"
PACKAGE_VERSION: ${{ steps.crate.outputs.version }}
run: |
npx @openapitools/openapi-generator-cli generate \
npx --yes @openapitools/openapi-generator-cli generate \
-i openapi.yaml \
-g rust \
-o . \
--additional-properties=packageName=hotdata,packageVersion=$PACKAGE_VERSION,library=reqwest,supportAsync=true \
--additional-properties=packageName=hotdata,packageVersion=${PACKAGE_VERSION},library=reqwest,supportAsync=true \
--http-user-agent "hotdata-rust/${PACKAGE_VERSION}" \
--skip-validate-spec

- name: Clean up fetched spec
run: rm -f openapi.yaml
- name: Clean up generated artifacts
run: |
rm -f openapi.yaml
rm -f git_push.sh

- name: Verify generated client compiles
run: cargo check
- name: Verify generated client (tests)
run: |
cargo test
cargo test --no-default-features --features rustls

- name: Create PR
uses: peter-evans/create-pull-request@v6
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8
with:
token: ${{ steps.app-token.outputs.token }}
title: "chore: regenerate client from updated OpenAPI spec"
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ reqwest = { version = "^0.13", default-features = false, features = ["json", "mu
default = ["native-tls"]
native-tls = ["reqwest/native-tls"]
rustls = ["reqwest/rustls"]

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
wiremock = "0.6"
46 changes: 46 additions & 0 deletions tests/smoke.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use hotdata::apis::configuration::{ApiKey, Configuration};
use hotdata::apis::workspaces_api;
use hotdata::models;
use wiremock::matchers::{header, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};

#[test]
fn error_model_deserializes() {
let e: models::Error = serde_json::from_str(r#"{"error":"not_found"}"#).unwrap();
assert_eq!(e.error, "not_found");
}

#[test]
fn configuration_matches_python_sdk_usage() {
let mut c = Configuration::new();
c.bearer_access_token = Some("YOUR_ACCESS_TOKEN".into());
c.api_key = Some(ApiKey {
prefix: None,
key: "YOUR_WORKSPACE_ID".into(),
});
assert!(c.bearer_access_token.is_some());
}

#[tokio::test]
async fn list_workspaces_hits_api_and_deserializes() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/workspaces"))
.and(header("authorization", "Bearer test-token"))
.respond_with(ResponseTemplate::new(200).set_body_raw(
r#"{"ok":true,"workspaces":[{"public_id":"ws_1","name":"Demo","active":true,"favorite":false,"provision_status":"ready","namespace":"demo"}]}"#,
"application/json",
))
.mount(&server)
.await;

let mut config = Configuration::new();
config.base_path = server.uri();
config.bearer_access_token = Some("test-token".into());

let body = workspaces_api::list_workspaces(&config, None).await.unwrap();
assert!(body.ok);
assert_eq!(body.workspaces.len(), 1);
assert_eq!(body.workspaces[0].public_id, "ws_1");
assert_eq!(body.workspaces[0].name, "Demo");
}