Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding OpenAPI Export and Rib to OpenAPI Converter #1206

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
0abe608
Adding OpenAPI Export and Rib to OpenAPI Converter
zelosleone Dec 22, 2024
3156eaf
Test2 (#2)
zelosleone Dec 24, 2024
ac845fc
Delete tests/client_generation_tests directory
zelosleone Dec 24, 2024
b502f26
Delete golem-worker-service-base/README.md
zelosleone Dec 24, 2024
bef7eee
Minor Code Updates, More on the way
zelosleone Dec 25, 2024
06aa0ee
Update rib_converter.rs
zelosleone Dec 25, 2024
c1e6929
Update openapi_swagger_tests.rs
zelosleone Dec 25, 2024
586b7b1
Update openapi_swagger_tests.rs
zelosleone Dec 25, 2024
aea1b7b
Deneme (#5)
zelosleone Dec 25, 2024
8453f7d
Enhance testing framework and OpenAPI integration
zelosleone Dec 26, 2024
703c651
Delete .github/workflows/openapi-tests.yaml
zelosleone Dec 26, 2024
ca1de38
Update Cargo.toml
zelosleone Dec 26, 2024
2ee4da7
Merge branch 'main' of https://github.com/zelosleone/golem
zelosleone Dec 26, 2024
d872a65
Update Cargo.toml
zelosleone Dec 26, 2024
c25cfa7
Merge branch 'main' of https://github.com/zelosleone/golem
zelosleone Dec 26, 2024
ece2a76
Refactor and enhance test suite for OpenAPI and JSON schema validatio…
zelosleone Dec 27, 2024
e4af0b6
Literally rented Hosting to test this
zelosleone Dec 27, 2024
e30da1e
.
zelosleone Dec 27, 2024
46d9777
Merge branch 'golemcloud:main' into main
zelosleone Dec 27, 2024
9407d2c
Merge branch 'golemcloud:main' into main
zelosleone Dec 28, 2024
be15e1d
Testing State, Gonna post more updated so its not finished yet
zelosleone Jan 1, 2025
1f85ada
16 End Points Worker Test Finally
zelosleone Jan 1, 2025
1a82213
Fix complex_wit_type_validation_tests to run with cargo test
zelosleone Jan 2, 2025
0c98323
Enhance API Definition and Swagger UI Functionality
zelosleone Jan 5, 2025
89573aa
Merge branch 'golemcloud:main' into main
zelosleone Jan 5, 2025
cdd123b
Enhance API Definition and Swagger UI Functionality
zelosleone Jan 5, 2025
31468e8
Copyright 2024-2025 Golem Cloud (#1215)
vigoo Jan 3, 2025
d767d97
Update api-response dependency to version 0.16.3 in Cargo.toml
zelosleone Jan 5, 2025
c86d532
.
zelosleone Jan 5, 2025
c74d9b1
Merge branch 'main' of https://github.com/zelosleone/golem
zelosleone Jan 5, 2025
02665f3
.
zelosleone Jan 5, 2025
bb55ef2
Revert "."
zelosleone Jan 5, 2025
7a291c2
Delete
zelosleone Jan 5, 2025
4f872ab
Delete Cargo.lock
zelosleone Jan 5, 2025
b82dbb8
Implement Swagger UI Binding and Enhance API Functionality
zelosleone Jan 6, 2025
c45ad48
Fixed CWE-754
zelosleone Jan 6, 2025
92ab25c
Merge branch 'golemcloud:main' into main
zelosleone Jan 6, 2025
990b288
Enhance Swagger UI Configuration on Golem-cli
zelosleone Jan 6, 2025
7f005ad
Updated Test Files After the whole deal of dynamic_linking update
zelosleone Jan 6, 2025
ce49dff
Test Files fixing testing
zelosleone Jan 6, 2025
e9dbd93
Had to Fix Tests and Command Line Generator too
zelosleone Jan 6, 2025
f6e68f0
More updated to be more compatible with the NEW PR that is merged wit…
zelosleone Jan 7, 2025
2a3b342
Refactor API Definition Commands and Enhance Swagger UI Integration (…
zelosleone Jan 7, 2025
f02dd3e
Enhance Test API and Update Worker Service Metadata
zelosleone Jan 7, 2025
58c0282
Enhance CORS Preflight Tests in API Gateway
zelosleone Jan 7, 2025
686f12e
Merge branch 'golemcloud:main' into main
zelosleone Jan 7, 2025
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
78 changes: 78 additions & 0 deletions .github/workflows/openapi-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: OpenAPI Tests
on:
push:
branches:
- main
pull_request:
paths:
- 'golem-worker-service-base/tests/openapi_swagger_tests.rs'
- '**/openapi/**'
- '**/swagger/**'

jobs:
openapi-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: recursive

- name: Fetch tag
run: git fetch origin --deepen=1

- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
components: rustfmt, clippy

- uses: Swatinem/rust-cache@v2
with:
shared-key: openapi-tests
cache-all-crates: true

- name: Install Protoc
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

- uses: davidB/rust-cargo-make@v1

- name: Build all targets
run: cargo make --profile ci build

- name: Build tests
run: cargo test --package golem-worker-service-base --test openapi_swagger_tests --no-run

- name: Create output directory
run: mkdir -p openapi_exports

- name: Run OpenAPI tests
run: |
RUST_LOG=info cargo test --package golem-worker-service-base --test openapi_swagger_tests -- --nocapture
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always

- name: Upload OpenAPI artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: openapi-exports
path: openapi_exports/

- name: Publish Test Report
uses: mikepenz/action-junit-report@v4
if: success() || failure()
with:
report_paths: '**/target/report-*.xml'
detailed_summary: true
include_passed: true

permissions:
contents: read
checks: write
pull-requests: write
10 changes: 9 additions & 1 deletion golem-worker-service-base/Cargo.toml
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I first tried harness=false, not noticing my tests would work better with harness=true, fixed that.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ harness = false
name = "api_gateway_end_to_end_tests"
harness = false


[[test]]
name = "openapi_swagger_tests"
harness = false

[dependencies]
golem-common = { path = "../golem-common" }
golem-api-grpc = { path = "../golem-api-grpc" }
Expand Down Expand Up @@ -83,6 +88,9 @@ tracing-subscriber = { workspace = true }
url = { workspace = true }
uuid = { workspace = true }
wasm-wave = { workspace = true }
utoipa = { version = "5.3.0", features = ["axum_extras", "yaml", "chrono", "uuid", "openapi_extensions"] }
utoipa-swagger-ui = { version = "8.1.0" }
indexmap = "2.2.3"

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
Expand All @@ -93,4 +101,4 @@ test-r = { workspace = true }

[[bench]]
name = "tree"
harness = false
harness = false
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use poem::{
web::{Path, Query, Data},
Result, handler,
};
use utoipa::openapi::OpenApi;
use crate::gateway_api_definition::http::{
openapi_export::OpenApiFormat,
openapi_converter::OpenApiConverter,
};
use poem::web::Json;

pub struct OpenApiHandler {
converter: OpenApiConverter,
}

impl OpenApiHandler {
pub fn new() -> Self {
Self {
converter: OpenApiConverter::new(),
}
}
}

#[handler]
pub async fn export_openapi(
Data(handler): Data<&OpenApiHandler>,
Path((id, version)): Path<(String, String)>,
Query(format): Query<OpenApiFormat>,
Json(openapi): Json<OpenApi>,
) -> Result<String> {
let content = handler.converter.exporter.export_openapi(&id, &version, openapi, &format);
Ok(content)
}
11 changes: 11 additions & 0 deletions golem-worker-service-base/src/gateway_api_definition/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,20 @@
pub use http_api_definition::*;
pub use http_api_definition_request::*;
pub use http_oas_api_definition::*;
pub use openapi_export::{OpenApiExporter, OpenApiFormat};
pub use openapi_converter::OpenApiConverter;
pub use rib_converter::{RibConverter, CustomSchemaType};
pub use swagger_ui::{
SwaggerUiConfig,
generate_swagger_ui,
};

mod http_api_definition;
mod http_api_definition_request;
mod http_oas_api_definition;
pub mod openapi_export;
pub mod openapi_converter;
pub mod rib_converter;
pub mod swagger_ui;
pub(crate) mod path_pattern_parser;
pub(crate) mod place_holder_parser;
Copy link
Author

@zelosleone zelosleone Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing as openapi export, utoipa is used for majority of the cases.

Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright 2024 Golem Cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;
use utoipa::openapi::OpenApi;
use crate::gateway_api_definition::http::openapi_export::OpenApiExporter;

pub struct OpenApiConverter {
pub exporter: Arc<OpenApiExporter>,
}

impl OpenApiConverter {
pub fn new() -> Self {
Self {
exporter: Arc::new(OpenApiExporter),
}
}

pub fn merge_openapi(mut base: OpenApi, other: OpenApi) -> OpenApi {
// Merge paths
base.paths.paths.extend(other.paths.paths);

// Merge components
match (&mut base.components, other.components.as_ref()) {
(Some(base_components), Some(other_components)) => {
// Merge schemas
base_components.schemas.extend(other_components.schemas.clone());
// Merge responses
base_components.responses.extend(other_components.responses.clone());
// Merge security schemes
base_components.security_schemes.extend(other_components.security_schemes.clone());
}
_ => base.components = other.components,
}

// Merge security requirements
match (&mut base.security, other.security.as_ref()) {
(Some(base_security), Some(other_security)) => {
base_security.extend(other_security.clone());
}
_ => base.security = other.security,
}

// Merge tags
match (&mut base.tags, other.tags.as_ref()) {
(Some(base_tags), Some(other_tags)) => {
base_tags.extend(other_tags.clone());
}
_ => base.tags = other.tags,
}

// Merge servers
match (&mut base.servers, other.servers.as_ref()) {
(Some(base_servers), Some(other_servers)) => {
base_servers.extend(other_servers.clone());
}
_ => base.servers = other.servers,
}

base
}
}

impl Default for OpenApiConverter {
fn default() -> Self {
Self::new()
}
}

#[cfg(test)]
mod tests {
use super::OpenApiConverter;
use utoipa::openapi::{
OpenApi,
Server,
Tag,
};

#[test]
fn test_openapi_converter() {
let _converter = OpenApiConverter::new();

// Create base OpenAPI
let mut base = OpenApi::new(Default::default(), ());
let get_op = OperationBuilder::new().summary(Some("Base operation".to_string())).build();
let mut path_item = PathItem::new(HttpMethod::Get, ());
path_item.get = Some(get_op);
base.paths.paths.insert("/base".to_string(), path_item);
let mut components = Components::new();
components.schemas.insert("BaseSchema".to_string(), Schema::Object(Object::new()).into());
base.components = Some(components);
base.security = Some(vec![SecurityRequirement::new("BaseAuth", ())]);
base.tags = Some(vec![Tag::new("base")]);
base.servers = Some(vec![Server::new("/base")]);

// Create other OpenAPI with duplicate path
let mut other = OpenApi::new(Default::default(), ());
let post_op = OperationBuilder::new().summary(Some("Other operation".to_string())).build();
let mut path_item = PathItem::new(HttpMethod::Get, ());
path_item.post = Some(post_op);
other.paths.paths.insert("/base".to_string(), path_item);
let mut components = Components::new();
components.schemas.insert("OtherSchema".to_string(), Schema::Object(Object::new()).into());
other.components = Some(components);
other.security = Some(vec![SecurityRequirement::new("OtherAuth", ())]);
other.tags = Some(vec![Tag::new("other")]);
other.servers = Some(vec![Server::new("/other")]);

// Test merging with duplicates
let merged = OpenApiConverter::merge_openapi(base.clone(), other.clone());

// Verify paths merged and duplicates handled
assert!(merged.paths.paths.contains_key("/base"));
let base_path = merged.paths.paths.get("/base").unwrap();
assert!(base_path.get.is_some(), "GET operation should be preserved");
assert!(base_path.post.is_some(), "POST operation should be added");

// Verify components merged
let components = merged.components.unwrap();
assert!(components.schemas.contains_key("BaseSchema"));
assert!(components.schemas.contains_key("OtherSchema"));

// Test empty component merging
let mut empty_base = OpenApi::new(Default::default(), ());
empty_base.components = None;
let merged = OpenApiConverter::merge_openapi(empty_base, other);
assert!(merged.components.is_some());
let components = merged.components.unwrap();
assert!(components.schemas.contains_key("OtherSchema"));
}

#[test]
fn test_openapi_converter_new() {
let converter = OpenApiConverter::new();
assert_eq!(Arc::strong_count(&converter.exporter), 1);
}

#[test]
fn test_merge_openapi_with_empty_fields() {
// Test merging when base has empty optional fields
let mut base = OpenApi::new(Default::default(), ());
base.security = None;
base.tags = None;
base.servers = None;
base.components = None;

// Create other OpenAPI with all fields populated
let mut other = OpenApi::new(Default::default(), ());
other.security = Some(vec![SecurityRequirement::new("OtherAuth", ())]);
other.tags = Some(vec![Tag::new("other")]);
other.servers = Some(vec![Server::new("/other")]);
let mut components = Components::new();
components.schemas.insert("OtherSchema".to_string(), Schema::Object(Object::new()).into());
other.components = Some(components);

let merged = OpenApiConverter::merge_openapi(base, other.clone());

// Verify all fields were properly merged
assert_eq!(merged.security, other.security);
assert_eq!(merged.tags, other.tags);
assert_eq!(merged.servers, other.servers);
assert_eq!(merged.components, other.components);
}
}
Loading