From 2bf549e32615f6844feaa907acf12bb4ec37a1a9 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Wed, 14 Aug 2024 14:39:11 +0200 Subject: [PATCH 1/3] Add api key support to python client + server --- README.md | 6 +++--- openapi.yaml | 9 +++++++++ python/remotebmi/client/client.py | 7 +++++-- python/remotebmi/server/__init__.py | 3 +++ python/remotebmi/server/auth.py | 11 +++++++++++ 5 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 python/remotebmi/server/auth.py diff --git a/README.md b/README.md index 9cc4c9f..8292200 100644 --- a/README.md +++ b/README.md @@ -68,21 +68,21 @@ If the port is different, you can pass the port as the `image_port` argument to Given you have a model class called `MyModel` in a package `mypackage` then the web service can be started with the following command. ```shell -BMI_MODULE=mypackage BMI_CLASS=MyModel run-bmi-server +BMI_MODULE=mypackage BMI_CLASS=MyModel BMI_API_KEY=somesecret run-bmi-server ``` For example [leakybucket](https://github.com/eWaterCycle/leakybucket-bmi): ```shell pip install leakybucket -BMI_MODULE=leakybucket.leakybucket_bmi BMI_CLASS=LeakyBucketBmi run-bmi-server +BMI_MODULE=leakybucket.leakybucket_bmi BMI_CLASS=LeakyBucketBmi BMI_API_KEY=somesecret run-bmi-server ``` and the client can connect to it with the following code. ```python > from remotebmi.client.client import RemoteBmiClient -> client = RemoteBmiClient('http://localhost:50051') +> client = RemoteBmiClient('http://localhost:50051', api_key='somesecret') > client.get_component_name() leakybucket ``` diff --git a/openapi.yaml b/openapi.yaml index c586b5a..b0c8ebc 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -16,6 +16,10 @@ info: servers: - url: http://localhost:50051 description: Default URL for a BMI service +security: + # TODO allow server in anonymous or apikey mode + # - {} + - apikey: [] tags: - name: IRF description: Initialize, run, finalize @@ -1264,3 +1268,8 @@ components: of the problem. It may or may not yield further information if dereferenced. additionalProperties: true + securitySchemes: + apikey: + type: apiKey + name: X-Auth + in: header diff --git a/python/remotebmi/client/client.py b/python/remotebmi/client/client.py index 9861435..f372b60 100644 --- a/python/remotebmi/client/client.py +++ b/python/remotebmi/client/client.py @@ -5,8 +5,11 @@ class RemoteBmiClient(Bmi): - def __init__(self, base_url): - self.client = Client(base_url=base_url) + def __init__(self, base_url, api_key=None): + headers = {} + if api_key: + headers["X-Auth"] = api_key + self.client = Client(base_url=base_url, headers=headers) def __del__(self): self.client.close() diff --git a/python/remotebmi/server/__init__.py b/python/remotebmi/server/__init__.py index 651149e..e2c3150 100644 --- a/python/remotebmi/server/__init__.py +++ b/python/remotebmi/server/__init__.py @@ -26,6 +26,9 @@ def make_app(model: Bmi): async def lifespan_handler(app: ConnexionMiddleware) -> AsyncIterator: # noqa: ARG001 yield {"model": model} + if environ.get("BMI_API_KEY", None): + environ["APIKEYINFO_FUNC"] = "remotebmi.server.auth.apikey_auth" + app = AsyncApp( "remotebmi.server", strict_validation=True, diff --git a/python/remotebmi/server/auth.py b/python/remotebmi/server/auth.py new file mode 100644 index 0000000..0075b67 --- /dev/null +++ b/python/remotebmi/server/auth.py @@ -0,0 +1,11 @@ +from os import environ + +from connexion.exceptions import OAuthProblem + + +def apikey_auth(token): + valid_key = environ.get("BMI_API_KEY") + if token != valid_key: + msg = "Invalid token" + raise OAuthProblem(msg) + return {"sub": "apikeyuser"} From c0eb01416fef2b2d8bad28e3bf3c1cd83d28a6cb Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Wed, 4 Sep 2024 08:37:42 +0200 Subject: [PATCH 2/3] Regenerate julia openapi server stubs --- RemoteBMI.jl/src/BmiServer.jl | 15 ++++---- RemoteBMI.jl/src/apis/api_GettersApi.jl | 4 +++ RemoteBMI.jl/src/apis/api_IRFApi.jl | 4 +-- RemoteBMI.jl/src/apis/api_SettersApi.jl | 8 +++-- .../src/apis/api_VariableInformationApi.jl | 12 +++++++ RemoteBMI.jl/src/modelincludes.jl | 7 ++-- .../models/model_BmiGetGridTypeResponse.jl | 9 ----- .../src/models/model_BmiInitializeRequest.jl | 30 ---------------- .../model_BmiSetValueAtIndicesRequest.jl | 34 ------------------ .../src/models/model_GetGridTypeResponse.jl | 9 +++++ .../src/models/model_GetVarTypeResponse.jl | 9 +++++ .../src/models/model_InitializeRequest.jl | 34 ++++++++++++++++++ .../src/models/model_ProblemDetails.jl | 21 ++++++++--- .../models/model_SetValueAtIndicesRequest.jl | 36 +++++++++++++++++++ 14 files changed, 140 insertions(+), 92 deletions(-) delete mode 100644 RemoteBMI.jl/src/models/model_BmiGetGridTypeResponse.jl delete mode 100644 RemoteBMI.jl/src/models/model_BmiInitializeRequest.jl delete mode 100644 RemoteBMI.jl/src/models/model_BmiSetValueAtIndicesRequest.jl create mode 100644 RemoteBMI.jl/src/models/model_GetGridTypeResponse.jl create mode 100644 RemoteBMI.jl/src/models/model_GetVarTypeResponse.jl create mode 100644 RemoteBMI.jl/src/models/model_InitializeRequest.jl create mode 100644 RemoteBMI.jl/src/models/model_SetValueAtIndicesRequest.jl diff --git a/RemoteBMI.jl/src/BmiServer.jl b/RemoteBMI.jl/src/BmiServer.jl index cdd310e..bc987a8 100644 --- a/RemoteBMI.jl/src/BmiServer.jl +++ b/RemoteBMI.jl/src/BmiServer.jl @@ -36,13 +36,13 @@ The following server methods must be implemented: - *signature:* get_grid_size(req::HTTP.Request, grid::Int64;) -> Int64 - **get_grid_type** - *invocation:* GET /get_grid_type/{grid} - - *signature:* get_grid_type(req::HTTP.Request, grid::Int64;) -> BmiGetGridTypeResponse + - *signature:* get_grid_type(req::HTTP.Request, grid::Int64;) -> GetGridTypeResponse - **finalize** - *invocation:* DELETE /finalize - *signature:* finalize(req::HTTP.Request;) -> Nothing - **initialize** - *invocation:* POST /initialize - - *signature:* initialize(req::HTTP.Request, bmi_initialize_request::BmiInitializeRequest;) -> Nothing + - *signature:* initialize(req::HTTP.Request, initialize_request::InitializeRequest;) -> Nothing - **update** - *invocation:* POST /update - *signature:* update(req::HTTP.Request;) -> Nothing @@ -63,7 +63,7 @@ The following server methods must be implemented: - *signature:* set_value(req::HTTP.Request, name::String, request_body::Vector{Float64};) -> Nothing - **set_value_at_indices** - *invocation:* POST /set_value_at_indices/{name} - - *signature:* set_value_at_indices(req::HTTP.Request, name::String, bmi_set_value_at_indices_request::BmiSetValueAtIndicesRequest;) -> Nothing + - *signature:* set_value_at_indices(req::HTTP.Request, name::String, set_value_at_indices_request::SetValueAtIndicesRequest;) -> Nothing - **get_current_time** - *invocation:* GET /get_current_time - *signature:* get_current_time(req::HTTP.Request;) -> Float64 @@ -123,7 +123,7 @@ The following server methods must be implemented: - *signature:* get_var_nbytes(req::HTTP.Request, name::String;) -> Int64 - **get_var_type** - *invocation:* GET /get_var_type/{name} - - *signature:* get_var_type(req::HTTP.Request, name::String;) -> String + - *signature:* get_var_type(req::HTTP.Request, name::String;) -> GetVarTypeResponse - **get_var_units** - *invocation:* GET /get_var_units/{name} - *signature:* get_var_units(req::HTTP.Request, name::String;) -> String @@ -187,10 +187,11 @@ function register(router::HTTP.Router, impl; path_prefix::String="", optional_mi end # export models -export BmiGetGridTypeResponse -export BmiInitializeRequest -export BmiSetValueAtIndicesRequest +export GetGridTypeResponse export GetVarLocationResponseLocation +export GetVarTypeResponse +export InitializeRequest export ProblemDetails +export SetValueAtIndicesRequest end # module BmiServer diff --git a/RemoteBMI.jl/src/apis/api_GettersApi.jl b/RemoteBMI.jl/src/apis/api_GettersApi.jl index dc407a6..d6ba826 100644 --- a/RemoteBMI.jl/src/apis/api_GettersApi.jl +++ b/RemoteBMI.jl/src/apis/api_GettersApi.jl @@ -17,6 +17,8 @@ function get_value_validate(handler) function get_value_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_value", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -46,6 +48,8 @@ function get_value_at_indices_validate(handler) function get_value_at_indices_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_value_at_indices", :minLength, openapi_params["name"], 1) + return handler(req) end end diff --git a/RemoteBMI.jl/src/apis/api_IRFApi.jl b/RemoteBMI.jl/src/apis/api_IRFApi.jl index a8b73a5..b2ad21e 100644 --- a/RemoteBMI.jl/src/apis/api_IRFApi.jl +++ b/RemoteBMI.jl/src/apis/api_IRFApi.jl @@ -31,7 +31,7 @@ end function initialize_read(handler) function initialize_read_handler(req::HTTP.Request) openapi_params = Dict{String,Any}() - openapi_params["BmiInitializeRequest"] = OpenAPI.Servers.to_param_type(BmiInitializeRequest, String(req.body)) + openapi_params["InitializeRequest"] = OpenAPI.Servers.to_param_type(InitializeRequest, String(req.body)) req.context[:openapi_params] = openapi_params return handler(req) @@ -49,7 +49,7 @@ end function initialize_invoke(impl; post_invoke=nothing) function initialize_invoke_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] - ret = impl.initialize(req::HTTP.Request, openapi_params["BmiInitializeRequest"];) + ret = impl.initialize(req::HTTP.Request, openapi_params["InitializeRequest"];) resp = OpenAPI.Servers.server_response(ret) return (post_invoke === nothing) ? resp : post_invoke(req, resp) end diff --git a/RemoteBMI.jl/src/apis/api_SettersApi.jl b/RemoteBMI.jl/src/apis/api_SettersApi.jl index 8931308..33de8ac 100644 --- a/RemoteBMI.jl/src/apis/api_SettersApi.jl +++ b/RemoteBMI.jl/src/apis/api_SettersApi.jl @@ -18,6 +18,8 @@ function set_value_validate(handler) function set_value_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "set_value", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -36,7 +38,7 @@ function set_value_at_indices_read(handler) openapi_params = Dict{String,Any}() path_params = HTTP.getparams(req) openapi_params["name"] = OpenAPI.Servers.to_param(String, path_params, "name", required=true, ) - openapi_params["BmiSetValueAtIndicesRequest"] = OpenAPI.Servers.to_param_type(BmiSetValueAtIndicesRequest, String(req.body)) + openapi_params["SetValueAtIndicesRequest"] = OpenAPI.Servers.to_param_type(SetValueAtIndicesRequest, String(req.body)) req.context[:openapi_params] = openapi_params return handler(req) @@ -47,6 +49,8 @@ function set_value_at_indices_validate(handler) function set_value_at_indices_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "set_value_at_indices", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -54,7 +58,7 @@ end function set_value_at_indices_invoke(impl; post_invoke=nothing) function set_value_at_indices_invoke_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] - ret = impl.set_value_at_indices(req::HTTP.Request, openapi_params["name"], openapi_params["BmiSetValueAtIndicesRequest"];) + ret = impl.set_value_at_indices(req::HTTP.Request, openapi_params["name"], openapi_params["SetValueAtIndicesRequest"];) resp = OpenAPI.Servers.server_response(ret) return (post_invoke === nothing) ? resp : post_invoke(req, resp) end diff --git a/RemoteBMI.jl/src/apis/api_VariableInformationApi.jl b/RemoteBMI.jl/src/apis/api_VariableInformationApi.jl index c1f6856..9ac76f8 100644 --- a/RemoteBMI.jl/src/apis/api_VariableInformationApi.jl +++ b/RemoteBMI.jl/src/apis/api_VariableInformationApi.jl @@ -17,6 +17,8 @@ function get_var_grid_validate(handler) function get_var_grid_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_var_grid", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -45,6 +47,8 @@ function get_var_itemsize_validate(handler) function get_var_itemsize_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_var_itemsize", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -73,6 +77,8 @@ function get_var_location_validate(handler) function get_var_location_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_var_location", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -101,6 +107,8 @@ function get_var_nbytes_validate(handler) function get_var_nbytes_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_var_nbytes", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -129,6 +137,8 @@ function get_var_type_validate(handler) function get_var_type_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_var_type", :minLength, openapi_params["name"], 1) + return handler(req) end end @@ -157,6 +167,8 @@ function get_var_units_validate(handler) function get_var_units_validate_handler(req::HTTP.Request) openapi_params = req.context[:openapi_params] + OpenAPI.validate_param("name", "get_var_units", :minLength, openapi_params["name"], 1) + return handler(req) end end diff --git a/RemoteBMI.jl/src/modelincludes.jl b/RemoteBMI.jl/src/modelincludes.jl index a5ceaec..d26f264 100644 --- a/RemoteBMI.jl/src/modelincludes.jl +++ b/RemoteBMI.jl/src/modelincludes.jl @@ -1,8 +1,9 @@ # This file was generated by the Julia OpenAPI Code Generator # Do not modify this file directly. Modify the OpenAPI specification instead. -include("models/model_BmiGetGridTypeResponse.jl") -include("models/model_BmiInitializeRequest.jl") -include("models/model_BmiSetValueAtIndicesRequest.jl") +include("models/model_GetGridTypeResponse.jl") include("models/model_GetVarLocationResponseLocation.jl") +include("models/model_GetVarTypeResponse.jl") +include("models/model_InitializeRequest.jl") include("models/model_ProblemDetails.jl") +include("models/model_SetValueAtIndicesRequest.jl") diff --git a/RemoteBMI.jl/src/models/model_BmiGetGridTypeResponse.jl b/RemoteBMI.jl/src/models/model_BmiGetGridTypeResponse.jl deleted file mode 100644 index 6badc26..0000000 --- a/RemoteBMI.jl/src/models/model_BmiGetGridTypeResponse.jl +++ /dev/null @@ -1,9 +0,0 @@ -# This file was generated by the Julia OpenAPI Code Generator -# Do not modify this file directly. Modify the OpenAPI specification instead. - - -if !isdefined(@__MODULE__, :BmiGetGridTypeResponse) - const BmiGetGridTypeResponse = String -else - @warn("Skipping redefinition of BmiGetGridTypeResponse to String") -end diff --git a/RemoteBMI.jl/src/models/model_BmiInitializeRequest.jl b/RemoteBMI.jl/src/models/model_BmiInitializeRequest.jl deleted file mode 100644 index 8d59ee9..0000000 --- a/RemoteBMI.jl/src/models/model_BmiInitializeRequest.jl +++ /dev/null @@ -1,30 +0,0 @@ -# This file was generated by the Julia OpenAPI Code Generator -# Do not modify this file directly. Modify the OpenAPI specification instead. - - -@doc raw"""bmiInitializeRequest - - BmiInitializeRequest(; - config_file=nothing, - ) - - - config_file::String -""" -Base.@kwdef mutable struct BmiInitializeRequest <: OpenAPI.APIModel - config_file::Union{Nothing, String} = nothing - - function BmiInitializeRequest(config_file, ) - OpenAPI.validate_property(BmiInitializeRequest, Symbol("config_file"), config_file) - return new(config_file, ) - end -end # type BmiInitializeRequest - -const _property_types_BmiInitializeRequest = Dict{Symbol,String}(Symbol("config_file")=>"String", ) -OpenAPI.property_type(::Type{ BmiInitializeRequest }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_BmiInitializeRequest[name]))} - -function check_required(o::BmiInitializeRequest) - true -end - -function OpenAPI.validate_property(::Type{ BmiInitializeRequest }, name::Symbol, val) -end diff --git a/RemoteBMI.jl/src/models/model_BmiSetValueAtIndicesRequest.jl b/RemoteBMI.jl/src/models/model_BmiSetValueAtIndicesRequest.jl deleted file mode 100644 index 0c65a00..0000000 --- a/RemoteBMI.jl/src/models/model_BmiSetValueAtIndicesRequest.jl +++ /dev/null @@ -1,34 +0,0 @@ -# This file was generated by the Julia OpenAPI Code Generator -# Do not modify this file directly. Modify the OpenAPI specification instead. - - -@doc raw"""bmiSetValueAtIndicesRequest - - BmiSetValueAtIndicesRequest(; - indices=nothing, - values=nothing, - ) - - - indices::Vector{Int64} - - values::Vector{Float64} -""" -Base.@kwdef mutable struct BmiSetValueAtIndicesRequest <: OpenAPI.APIModel - indices::Union{Nothing, Vector{Int64}} = nothing - values::Union{Nothing, Vector{Float64}} = nothing - - function BmiSetValueAtIndicesRequest(indices, values, ) - OpenAPI.validate_property(BmiSetValueAtIndicesRequest, Symbol("indices"), indices) - OpenAPI.validate_property(BmiSetValueAtIndicesRequest, Symbol("values"), values) - return new(indices, values, ) - end -end # type BmiSetValueAtIndicesRequest - -const _property_types_BmiSetValueAtIndicesRequest = Dict{Symbol,String}(Symbol("indices")=>"Vector{Int64}", Symbol("values")=>"Vector{Float64}", ) -OpenAPI.property_type(::Type{ BmiSetValueAtIndicesRequest }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_BmiSetValueAtIndicesRequest[name]))} - -function check_required(o::BmiSetValueAtIndicesRequest) - true -end - -function OpenAPI.validate_property(::Type{ BmiSetValueAtIndicesRequest }, name::Symbol, val) -end diff --git a/RemoteBMI.jl/src/models/model_GetGridTypeResponse.jl b/RemoteBMI.jl/src/models/model_GetGridTypeResponse.jl new file mode 100644 index 0000000..5cc00f6 --- /dev/null +++ b/RemoteBMI.jl/src/models/model_GetGridTypeResponse.jl @@ -0,0 +1,9 @@ +# This file was generated by the Julia OpenAPI Code Generator +# Do not modify this file directly. Modify the OpenAPI specification instead. + + +if !isdefined(@__MODULE__, :GetGridTypeResponse) + const GetGridTypeResponse = String +else + @warn("Skipping redefinition of GetGridTypeResponse to String") +end diff --git a/RemoteBMI.jl/src/models/model_GetVarTypeResponse.jl b/RemoteBMI.jl/src/models/model_GetVarTypeResponse.jl new file mode 100644 index 0000000..5d02a37 --- /dev/null +++ b/RemoteBMI.jl/src/models/model_GetVarTypeResponse.jl @@ -0,0 +1,9 @@ +# This file was generated by the Julia OpenAPI Code Generator +# Do not modify this file directly. Modify the OpenAPI specification instead. + + +if !isdefined(@__MODULE__, :GetVarTypeResponse) + const GetVarTypeResponse = String +else + @warn("Skipping redefinition of GetVarTypeResponse to String") +end diff --git a/RemoteBMI.jl/src/models/model_InitializeRequest.jl b/RemoteBMI.jl/src/models/model_InitializeRequest.jl new file mode 100644 index 0000000..47f1246 --- /dev/null +++ b/RemoteBMI.jl/src/models/model_InitializeRequest.jl @@ -0,0 +1,34 @@ +# This file was generated by the Julia OpenAPI Code Generator +# Do not modify this file directly. Modify the OpenAPI specification instead. + + +@doc raw"""InitializeRequest + + InitializeRequest(; + config_file=nothing, + ) + + - config_file::String : Path to the configuration file. Should resolvable by web service. +""" +Base.@kwdef mutable struct InitializeRequest <: OpenAPI.APIModel + config_file::Union{Nothing, String} = nothing + + function InitializeRequest(config_file, ) + OpenAPI.validate_property(InitializeRequest, Symbol("config_file"), config_file) + return new(config_file, ) + end +end # type InitializeRequest + +const _property_types_InitializeRequest = Dict{Symbol,String}(Symbol("config_file")=>"String", ) +OpenAPI.property_type(::Type{ InitializeRequest }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_InitializeRequest[name]))} + +function check_required(o::InitializeRequest) + o.config_file === nothing && (return false) + true +end + +function OpenAPI.validate_property(::Type{ InitializeRequest }, name::Symbol, val) + if name === Symbol("config_file") + OpenAPI.validate_param(name, "InitializeRequest", :minLength, val, 0) + end +end diff --git a/RemoteBMI.jl/src/models/model_ProblemDetails.jl b/RemoteBMI.jl/src/models/model_ProblemDetails.jl index 42c2d28..3aa9de7 100644 --- a/RemoteBMI.jl/src/models/model_ProblemDetails.jl +++ b/RemoteBMI.jl/src/models/model_ProblemDetails.jl @@ -3,6 +3,7 @@ @doc raw"""ProblemDetails +Definition from https://datatracker.ietf.org/doc/html/rfc9457#name-json-schema-for-http-proble ProblemDetails(; type=nothing, @@ -12,11 +13,11 @@ instance=nothing, ) - - type::String - - title::String - - status::Int64 - - detail::String - - instance::String + - type::String : A URI reference that identifies the problem type. + - title::String : A short, human-readable summary of the problem type. + - status::Int64 : The HTTP status code generated by the origin server for this occurrence of the problem. + - detail::String : A human-readable explanation specific to this occurrence of the problem. + - instance::String : A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. """ Base.@kwdef mutable struct ProblemDetails <: OpenAPI.APIModel type::Union{Nothing, String} = nothing @@ -43,4 +44,14 @@ function check_required(o::ProblemDetails) end function OpenAPI.validate_property(::Type{ ProblemDetails }, name::Symbol, val) + if name === Symbol("type") + OpenAPI.validate_param(name, "ProblemDetails", :format, val, "uri-reference") + end + if name === Symbol("status") + OpenAPI.validate_param(name, "ProblemDetails", :maximum, val, 599, false) + OpenAPI.validate_param(name, "ProblemDetails", :minimum, val, 100, false) + end + if name === Symbol("instance") + OpenAPI.validate_param(name, "ProblemDetails", :format, val, "uri-reference") + end end diff --git a/RemoteBMI.jl/src/models/model_SetValueAtIndicesRequest.jl b/RemoteBMI.jl/src/models/model_SetValueAtIndicesRequest.jl new file mode 100644 index 0000000..5ec5ffe --- /dev/null +++ b/RemoteBMI.jl/src/models/model_SetValueAtIndicesRequest.jl @@ -0,0 +1,36 @@ +# This file was generated by the Julia OpenAPI Code Generator +# Do not modify this file directly. Modify the OpenAPI specification instead. + + +@doc raw"""SetValueAtIndicesRequest + + SetValueAtIndicesRequest(; + indices=nothing, + values=nothing, + ) + + - indices::Vector{Int64} + - values::Vector{Float64} +""" +Base.@kwdef mutable struct SetValueAtIndicesRequest <: OpenAPI.APIModel + indices::Union{Nothing, Vector{Int64}} = nothing + values::Union{Nothing, Vector{Float64}} = nothing + + function SetValueAtIndicesRequest(indices, values, ) + OpenAPI.validate_property(SetValueAtIndicesRequest, Symbol("indices"), indices) + OpenAPI.validate_property(SetValueAtIndicesRequest, Symbol("values"), values) + return new(indices, values, ) + end +end # type SetValueAtIndicesRequest + +const _property_types_SetValueAtIndicesRequest = Dict{Symbol,String}(Symbol("indices")=>"Vector{Int64}", Symbol("values")=>"Vector{Float64}", ) +OpenAPI.property_type(::Type{ SetValueAtIndicesRequest }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_SetValueAtIndicesRequest[name]))} + +function check_required(o::SetValueAtIndicesRequest) + o.indices === nothing && (return false) + o.values === nothing && (return false) + true +end + +function OpenAPI.validate_property(::Type{ SetValueAtIndicesRequest }, name::Symbol, val) +end From bb8d8808db9d66ce496005279e0a26d2d6cfd0f1 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Wed, 4 Sep 2024 13:29:15 +0200 Subject: [PATCH 3/3] Use generated types + stuck at return string not being jsonified and apikey not in stub --- RemoteBMI.jl/README.md | 5 +++++ RemoteBMI.jl/example/README.md | 10 ++++++---- RemoteBMI.jl/example/heat_bmi_server.jl | 5 +++-- RemoteBMI.jl/src/RemoteBMI.jl | 18 +++++++++++++++--- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/RemoteBMI.jl/README.md b/RemoteBMI.jl/README.md index f3c26db..9e95b86 100644 --- a/RemoteBMI.jl/README.md +++ b/RemoteBMI.jl/README.md @@ -37,4 +37,9 @@ The openapi server stubs where generated using the following command: ```shell npx @openapitools/openapi-generator-cli generate -i ./openapi.yaml -g julia-server -o julia-server --additional-properties=packageName=BmiServer --additional-properties=exportModels=true # Copy the generated files to RemoteBMI.jl/src/ + +# TODO find out why api key is not in the generated code +# At example https://github.com/JuliaComputing/OpenAPI.jl/blob/2d447986b6377a6724ae59380f087c2b270e7795/test/server/openapigenerator_petstore_v3/petstore/src/apis/api_PetApi.jl#L56 +# It is given to impl function as second argument +# but in the generated code it is missing ``` \ No newline at end of file diff --git a/RemoteBMI.jl/example/README.md b/RemoteBMI.jl/example/README.md index ae6df17..292ee82 100644 --- a/RemoteBMI.jl/example/README.md +++ b/RemoteBMI.jl/example/README.md @@ -11,6 +11,7 @@ dev .. CTRL-D # Run server export BMI_SERVER_PORT=50555 +export BMI_SERVER_SECRET="somesecret" julia --project=$PWD heat_bmi_server.jl ``` @@ -24,16 +25,17 @@ Interact with it using the Python client. ```python from remotebmi.client.client import RemoteBmiClient -from remotebmi.client.reserve import reserve_values +from remotebmi.reserve import reserve_values client = RemoteBmiClient('http://localhost:50555') -# TODO use placeholder for path +# TODO use placeholder for path instead of path on my machine # client.initialize('/heat.toml') -client.initialize('/home/stefanv/git/eWaterCycle/remotebmi/python/heat.toml') -# TODO Julia server throws error here +client.initialize('/home/verhoes/git/eWaterCycle/remotebmi/python/heat.toml') +# TODO eget_component_name errors because server returns plain/text instead of json client.get_component_name() client.update() client.get_current_time() +client.get_var_type('plate_surface__temperature') dest = reserve_values(client, 'plate_surface__temperature') r = client.get_value('plate_surface__temperature', dest) r diff --git a/RemoteBMI.jl/example/heat_bmi_server.jl b/RemoteBMI.jl/example/heat_bmi_server.jl index 754d094..1ece1cc 100644 --- a/RemoteBMI.jl/example/heat_bmi_server.jl +++ b/RemoteBMI.jl/example/heat_bmi_server.jl @@ -4,5 +4,6 @@ using Heat import RemoteBMI -port = parse(Int, get(ENV, "BMI_PORT", "50051")) -RemoteBMI.run(Heat.Model, "0.0.0.0", port) +port = parse(Int, get(ENV, "BMI_SERVER_PORT", "50051")) +secret = get(ENV, "BMI_SERVER_SECRET", "") +RemoteBMI.run(Heat.Model, "0.0.0.0", port, secret) diff --git a/RemoteBMI.jl/src/RemoteBMI.jl b/RemoteBMI.jl/src/RemoteBMI.jl index ce4be37..7f09017 100644 --- a/RemoteBMI.jl/src/RemoteBMI.jl +++ b/RemoteBMI.jl/src/RemoteBMI.jl @@ -2,6 +2,17 @@ module RemoteBMI using HTTP +# Treat returned string as something that must be JSON serialized +# At https://github.com/JuliaComputing/OpenAPI.jl/blob/2d447986b6377a6724ae59380f087c2b270e7795/src/server.jl#L167 +# string is force tod return as plain text +# Treat returned string from route implementations as thing that must be JSON serialized +# Without this, the string would be returned as plain text +#using OpenAPI +#using OpenAPI.Servers +#OpenAPI.Servers.server_response(resp::AbstractString, headers=HTTP.Headers()) = OpenAPI.Servers.server_response(OpenAPI.to_json(resp), [Pair("Content-Type", "application/json")]) +# TODO find way that does not break OpenAPI.Servers.server_response as with overwrite a client request is timing out +# TODO Alternative is to return plain/text in paths that return strings + include("./BmiServer.jl") using .BmiServer @@ -9,7 +20,7 @@ import BasicModelInterface as BMI # TODO move route implementations to own module -function initialize(req::HTTP.Request, bmi_initialize_request::BmiInitializeRequest)::Nothing +function initialize(req::HTTP.Request, bmi_initialize_request::InitializeRequest)::Nothing global m m = BMI.initialize(MyModel, bmi_initialize_request.config_file) return nothing @@ -119,6 +130,7 @@ function reserve_grid_coords(m, grid::Int64, dim_index::Int8)::Vector{Float64} size = BMI.get_grid_node_count(m, grid) else error("Unsupported grid type: $mtype") + end return zeros(Float64, size) end @@ -141,7 +153,7 @@ function set_value(req::HTTP.Request, name::String, request_body::Vector{Float64 BMI.set_value(m, name, request_body) end -function set_value_at_indices(req::HTTP.Request, name::String, bmi_set_value_at_indices_request::BmiSetValueAtIndicesRequest;)::Nothing +function set_value_at_indices(req::HTTP.Request, name::String, bmi_set_value_at_indices_request::SetValueAtIndicesRequest;)::Nothing BMI.set_value_at_indices(m, name, bmi_set_value_at_indices_request) end @@ -274,7 +286,7 @@ function get_var_units(req::HTTP.Request, name::String;)::String return BMI.get_var_units(m, name) end -function run(model, host, port) +function run(model, host, port, trusted_apikey) global MyModel = model try router = HTTP.Router()