From 706046ab4a128986ff30e9d85eedd4c68a10fb6b Mon Sep 17 00:00:00 2001 From: Danglewood <85772166+deeleeramone@users.noreply.github.com> Date: Tue, 18 Mar 2025 16:15:19 -0700 Subject: [PATCH] [Feature] `openbb-platform-api` - Create Entry In `widgets.json` For Forms (#7055) * define pattern for automating widget entry for forms * Add screenshot * linters * linters * linters * too many locals * linters * linters * difference between date and _date * more to caps strings * catch exclude in params config * skip finding columnsDefs for multi file viewer * catch edge case * fix tests --------- Co-authored-by: Theodore Aptekarev --- .../extensions/platform_api/README.md | 106 +++++++ .../openbb_platform_api/query_models.py | 15 + .../openbb_platform_api/utils/openapi.py | 224 +++++++++++++- .../openbb_platform_api/utils/widgets.py | 170 +++++++++-- .../platform_api/tests/mock_openapi.json | 165 +++++++++++ .../platform_api/tests/mock_widgets.json | 279 +++++++++++++----- .../platform_api/tests/test_openapi_utils.py | 162 ++++------ 7 files changed, 904 insertions(+), 217 deletions(-) create mode 100644 openbb_platform/extensions/platform_api/openbb_platform_api/query_models.py diff --git a/openbb_platform/extensions/platform_api/README.md b/openbb_platform/extensions/platform_api/README.md index 2a3f8f8a08d..c58ea0c53fc 100644 --- a/openbb_platform/extensions/platform_api/README.md +++ b/openbb_platform/extensions/platform_api/README.md @@ -418,6 +418,112 @@ async def hello_chart() -> dict: return fig.to_plotly_json() ``` +### Form Submit Widget + +When submitted, Workspace makes a POST request to the endpoint. + +If the POST function returns a 200 status code, the widget associated with the GET function is refreshed. + +The results of the GET function does not have to correspond with the parameters and results of the POST function. + +For example, the response to submitting a form can be a Markdown widget with a custom message. + +The entry in `widgets.json` will be automatically created if the conditions below are met: + +- GET and POST methods must share the same API route. +- POST method takes 1 positional argument, a sub-class of Pydantic BaseModel. + - Create a model, like annotated table fields, defining all inputs to the form. + +#### Example + +The code below creates a widget with a form as the input, and an output table of all submitted forms, as processed through the `IntakeForm` model. + +```python +import uuid +from datetime import date as dateType +from typing import Literal, Union + +# from fastapi import FastAPI +from openbb_platform_api.query_models import FormData +from openbb_platform_api.response_models import Data +from pydantic import ConfigDict, Field + +# app = FastAPI() + +AccountTypes = Literal["General Fund", "Separately Managed", "Private Equity", "Family Office"] + +class GeneralIntake(FormData): + """Submit a form via POST request.""" + + date_created: dateType = Field( + title="Created On", default_factory=dateType.today + ) + first_name: str = Field(title="First Name") + last_name: str = Field(title="Last Name") + email: str = Field(title="Contact Email") + dob: dateType = Field( + title="Date Of Birth", + ) + account_types: Union[AccountTypes, list[AccountTypes]] = Field( + title="Type Of Account", + json_schema_extra={ + "x-widget_config": {"multiSelect": True}, + }, + ) + submit: bool = Field( + default=True, + title="Submit", + type="button", # This creates a button, when pressed the parameter is sent as True + ) + + +class IntakeForm(Data): + """Submission Records.""" + + model_config = ConfigDict(extra="ignore") + + contacted: bool = Field( + title="Contacted", + default=False, + ) + date_created: dateType = Field( + title="Created On", + ) + first_name: str = Field(title="First Name") + last_name: str = Field(title="Last Name") + email: str = Field(title="Contact Email") + dob: dateType = Field( + title="Date Of Birth", + ) + account_types: Union[AccountTypes, list[AccountTypes]] = Field( + title="Account Interest", + ) + unique_id: uuid.UUID = Field( + title="Unique ID", + default_factory=uuid.uuid4, + ) + + +INTAKE_FORMS: list[IntakeForm] = [] + + +@app.post("/general_intake") +async def general_intake_post(data: GeneralIntake) -> bool: + global INTAKE_FORMS + try: + INTAKE_FORMS.append(IntakeForm(**data.model_dump())) + return True + except Exception as e: + raise e from e + + +@app.get("/general_intake") +async def general_intake() -> list[IntakeForm]: + return INTAKE_FORMS +``` + +Screenshot 2025-03-09 at 9 51 47 PM + ## Widget Config Any value from the [`widgets.json`](https://docs.openbb.co/terminal/custom-backend/widgets-json-reference) structure can be passed into the `@app` decorator by including an `openapi_extra` dictionary with the key, `"widget_config"`. diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/query_models.py b/openbb_platform/extensions/platform_api/openbb_platform_api/query_models.py new file mode 100644 index 00000000000..e2a041b6353 --- /dev/null +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/query_models.py @@ -0,0 +1,15 @@ +"""OpenBB Workspace Query Models.""" + +from openbb_core.provider.abstract.data import Data +from pydantic import AliasGenerator, ConfigDict +from pydantic.alias_generators import to_snake + + +class FormData(Data): + """Submit a form via POST request.""" + + model_config = ConfigDict( + extra="allow", + alias_generator=AliasGenerator(to_snake), + title="Submit Form", + ) diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py index 81b7b571af2..6d33fbb65fb 100644 --- a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/openapi.py @@ -55,6 +55,10 @@ TO_CAPS_STRINGS = [ "Figi", "Cusip", "Pdf", + "Otm", + "Atm", + "Itm", + "Fomc", ] @@ -109,7 +113,7 @@ def set_parameter_type(p: dict, p_schema: dict): ): p["type"] = "boolean" - if "date" in p["parameter_name"]: + if p["parameter_name"] == "date" or "_date" in p["parameter_name"]: p["type"] = "date" if "timeframe" in p["parameter_name"]: @@ -118,13 +122,15 @@ def set_parameter_type(p: dict, p_schema: dict): if p["parameter_name"] == "limit": p["type"] = "number" - if p.get("type") in ("array", "list") or isinstance(p.get("type"), list): + if p.get("type") in ("array", "list") or isinstance(p.get("type"), (list, dict)): p["type"] = "text" return p -def set_parameter_options(p: dict, p_schema: dict, providers: list[str]) -> dict: +def set_parameter_options( # pylint: disable=too-many-branches + p: dict, p_schema: dict, providers: list[str] +) -> dict: """ Set options for the parameter based on the schema. @@ -142,7 +148,11 @@ def set_parameter_options(p: dict, p_schema: dict, providers: list[str]) -> dict Dict Updated parameter dictionary with options. """ - choices: dict[str, list[dict[str, str]]] = {} + choices: dict[str, list[dict[str, str]]] = ( + p.get("options", {}) + if p.get("options") + else p_schema.get("options", {}) if p_schema.get("options") else {} + ) widget_configs: dict[str, dict] = {} multiple_items_allowed_dict: dict = {} is_provider_specific = False @@ -164,6 +174,7 @@ def set_parameter_options(p: dict, p_schema: dict, providers: list[str]) -> dict elif len(providers) == 1 and "enum" in p_schema: provider_choices = p_schema["enum"] p_schema.pop("enum") + if provider_choices: choices[provider] = [ {"label": str(c), "value": c} for c in provider_choices @@ -175,7 +186,9 @@ def set_parameter_options(p: dict, p_schema: dict, providers: list[str]) -> dict # Check title for provider-specific information if "title" in p_schema: - title_providers = set(p_schema["title"].split(",")) + title_providers = ( + set(p_schema["title"].split(",")) if p_schema.get("title") else set() + ) if title_providers.intersection(providers): is_provider_specific = True available_providers.update(title_providers) @@ -235,10 +248,12 @@ def set_parameter_options(p: dict, p_schema: dict, providers: list[str]) -> dict if provider in p_schema and p_schema[provider].get("x-widget_config"): widget_configs[provider] = p_schema[provider].get("x-widget_config") - p["options"] = choices p["multiple_items_allowed"] = multiple_items_allowed_dict - if is_provider_specific: + if choices: + p["options"] = choices + + if is_provider_specific and len(available_providers) > 1: p["available_providers"] = list(available_providers) p["x-widget_config"] = widget_configs @@ -273,11 +288,17 @@ def process_parameter(param: dict, providers: list[str]) -> dict: param_name.replace("_", " ").replace("fixedincome", "fixed income").title() ) p["description"] = ( - (param.get("description", param_name).split(" (provider:")[0].strip()) - .split("Multiple comma separated items allowed")[0] - .strip() + ( + (param.get("description", param_name).split(" (provider:")[0].strip()) + .split("Multiple comma separated items allowed")[0] + .strip() + ) + if param.get("description") + else "" ) p["optional"] = param.get("required", False) is False + p["type"] = param.get("type", "text") + p["value"] = param.get("value") if param_name == "provider": p["type"] = "text" @@ -314,12 +335,31 @@ def process_parameter(param: dict, providers: list[str]) -> dict: return p - p_schema = param.get("schema", {}) + if x_widget_config := param.get("x-widget_config", {}): + p["x-widget_config"] = x_widget_config + + p_schema = param.get("schema", {}) or param p["value"] = p_schema.get("default", None) + + custom = ( + param.get("options", {}).get("custom", []) + if "options" in param and "custom" in param["options"] + else [] + ) + + if custom: + p["options"] = {"custom": custom} + p = set_parameter_options(p, p_schema, providers) p = set_parameter_type(p, p_schema) + if "options" not in p: + p["options"] = [] + if p_schema.get("options", []): + p["options"] = p_schema["options"] + if title := p_schema.get("title", ""): + p["label"] = ( p.get("parameter_name", "").replace("_", " ").title() if ("," in title and title.replace(",", "").islower()) @@ -328,6 +368,10 @@ def process_parameter(param: dict, providers: list[str]) -> dict: ) if _widget_config := p_schema.get("x-widget_config", {}): + for provider in providers: + if provider in _widget_config: + _widget_config = _widget_config[provider] + break p.update(_widget_config) return p @@ -353,8 +397,7 @@ def get_query_schema_for_widget( """ has_chart = False command = openapi_json["paths"][command_route] - schema_method = list(command)[0] - command = command[schema_method] + command = command.get("get", {}) params = command.get("parameters", []) route_params: list[dict] = [] providers: list[str] = extract_providers(params) @@ -370,7 +413,9 @@ def get_query_schema_for_widget( p = process_parameter(param, providers) if "show" not in p: p["show"] = True - route_params.append(p) + + if not p.get("exclude") and not p.get("x-widget_config", {}).get("exclude"): + route_params.append(p) return route_params, has_chart @@ -416,7 +461,7 @@ def get_data_schema_for_widget(openapi_json, operation_id, route: Optional[str] if isinstance(response_ref, dict) and "type" in response_ref: response_ref = response_ref["type"] - if response_ref: + if response_ref and isinstance(response_ref, str): # Extract the schema name from the reference schema_name = response_ref.split("/")[-1] # Fetch and return the schema from components @@ -586,7 +631,7 @@ def data_schema_to_columns_defs( # noqa: PLR0912 # pylint: disable=too-many-br column_def["headerName"] = " ".join( [ (word.upper() if word in TO_CAPS_STRINGS else word) - for word in header_name.split(" ") + for word in header_name.replace("_", " ").split(" ") ] ) column_def["headerTooltip"] = prop.get( @@ -663,3 +708,150 @@ def data_schema_to_columns_defs( # noqa: PLR0912 # pylint: disable=too-many-br column_defs.append(column_def) return column_defs + + +def post_query_schema_for_widget( + openapi_json, + operation_id, + route: Optional[str] = None, + target_schema: Optional[str] = None, +): + """ + Get the POST query schema for a widget based on its operationId. + + Args: + openapi (dict): The OpenAPI specification as a dictionary. + operation_id (str): The operationId of the widget. + route (str): The route of the widget, if any. + target_schema (str): The target schema to extract, if any. + + Returns: + list[dict]: The schema dictionary for the widget's data. + """ + + new_params: dict = {} + + def set_param(k, v): + """Set the parameter.""" + nonlocal new_params + + new_params[k] = {} + new_params[k]["name"] = k + new_params[k]["type"] = ( + "text" + if v.get("type") == "object" + else "date" if "date" in v.get("format", "") else v.get("type", "text") + ) + new_params[k]["title"] = v.get("title") + new_params[k]["description"] = v.get("description") + new_params[k]["default"] = v.get("default") + new_params[k]["x-widget_config"] = v.get("x-widget_config", {}) + choices: list = ( + [{"label": c, "value": c} for c in v.get("choices", []) if c] + if v.get("choices") + else [] + ) + + if isinstance(v, dict) and "anyOf" in v: + param_types = [] + for item in v["anyOf"]: + if "type" in item and item.get("type") != "null": + param_types.append(item["type"]) + if "enum" in item: + choices.extend({"label": c, "value": c} for c in item["enum"]) + + if param_types: + new_params[k]["type"] = ( + "number" + if "number" in param_types + or "integer" in param_types + and "string" not in param_types + and "date" not in param_types + else ( + "date" + if any( + "date" in sub_prop.get("format", "") + for sub_prop in v["anyOf"] + if isinstance(sub_prop, dict) + ) + else "text" + ) + ) + else: + new_params[k]["type"] = ( + "text" + if v.get("type") == "object" + else ( + "date" + if "date" in v.get("format", "") + else v.get("type", "text") + ) + ) + elif isinstance(v, dict) and "enum" in v: + choices.extend({"label": c, "value": c} for c in v["enum"] if c) + + if choices: + new_params[k]["options"] = {"custom": choices} + + if not route: + for path, methods in openapi_json["paths"].items(): + for _method, details in methods.items(): + if details.get("operationId") == operation_id: + route = path + break + + _route = openapi_json["paths"].get(route, {}).get("post", {}) + + if ( + schema := _route.get("requestBody", {}) + .get("content", {}) + .get("application/json", {}) + .get("schema", {}) + ): + # Get the reference to the schema for the request body. + + param_ref = ( + schema["items"].get("$ref") + if "items" in schema + else schema.get("$ref") or schema + ) + + if isinstance(param_ref, dict) and "type" in param_ref: + param_ref = param_ref["type"] + + if param_ref: + # Extract the schema name from the reference + schema_name = param_ref.split("/")[-1] + schema = openapi_json["components"]["schemas"].get(schema_name, schema_name) + props = schema.get("properties", {}) + + for k, v in props.items(): + if target_schema and target_schema != k: + continue + if nested_schema := v.get("$ref"): + nested_schema_name = nested_schema.split("/")[-1] + nested_schema = openapi_json["components"]["schemas"].get( + nested_schema_name, {} + ) + for nested_k, nested_v in nested_schema.get( + "properties", {} + ).items(): + set_param(nested_k, nested_v) + + else: + set_param(k, v) + + route_params: list[dict] = [] + providers = ["custom"] + + for param in list(new_params): + p = process_parameter(new_params[param], providers) + if not p.get("exclude") and not p.get("x-widget_config", {}).get( + "exclude" + ): + route_params.append(p) + + return route_params + + # Return None if the schema is not found + return None diff --git a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py index 9218900c7d1..31d6f4c80c0 100644 --- a/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py +++ b/openbb_platform/extensions/platform_api/openbb_platform_api/utils/widgets.py @@ -99,7 +99,6 @@ def modify_query_schema(query_schema: list[dict], provider_value: str): _item = deepcopy(item) provider_value_options: dict = {} provider_value_widget_config: dict = {} - # Exclude provider parameter. Those will be added last. if "parameter_name" in _item and _item["parameter_name"] == "provider": continue @@ -111,17 +110,22 @@ def modify_query_schema(query_schema: list[dict], provider_value: str): ): continue - if provider_value in _item["multiple_items_allowed"] and _item[ - "multiple_items_allowed" - ].get(provider_value, False): + if ( + provider_value + and isinstance(_item, dict) + and provider_value in _item.get("multiple_items_allowed", {}) + and _item.get("multiple_items_allowed", {}).get(provider_value, False) + ): _item["description"] = ( _item["description"] + " Multiple comma separated items allowed." ) _item["type"] = "text" _item["multiSelect"] = True - if "options" in _item: - provider_value_options = _item.pop("options") + if "options" in _item and _item.get("options"): + provider_value_options = _item.pop("options", None) + if isinstance(provider_value_options, list): + provider_value_options = {provider_value: provider_value_options} if provider_value in provider_value_options and bool( provider_value_options[provider_value] @@ -132,12 +136,12 @@ def modify_query_schema(query_schema: list[dict], provider_value: str): _item["options"] = provider_value_options["other"] _item["type"] = "text" - _item.pop("multiple_items_allowed") + _ = _item.pop("multiple_items_allowed", None) if "available_providers" in _item: _item.pop("available_providers") - _item["paramName"] = _item.pop("parameter_name") + _item["paramName"] = _item.pop("parameter_name", None) if not _item.get("label") and _item["paramName"] in [ "url", @@ -158,8 +162,9 @@ def modify_query_schema(query_schema: list[dict], provider_value: str): ) if "x-widget_config" in _item: - provider_value_widget_config = _item.pop("x-widget_config") - _item.update(provider_value_widget_config) + provider_value_widget_config[ + provider_value if provider_value else "custom" + ] = _item.pop("x-widget_config", {}) if ( provider_value_widget_config @@ -185,7 +190,7 @@ def modify_query_schema(query_schema: list[dict], provider_value: str): return modified_query_schema -def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches +def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches, too-many-locals openapi: dict, widget_exclude_filter: list ): """Build the widgets.json file.""" @@ -194,6 +199,7 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches TO_CAPS_STRINGS, data_schema_to_columns_defs, get_query_schema_for_widget, + post_query_schema_for_widget, ) if not openapi: @@ -212,6 +218,12 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches if openapi["paths"].get(p, {}) and "get" in openapi["paths"][p] ] for route in routes: + has_form_endpoint = False + form_route: dict = {} + if "post" in openapi["paths"][route] and "get" in openapi["paths"][route]: + has_form_endpoint = True + form_route = openapi["paths"][route]["post"] + skip = False for starred in starred_list: if route.startswith( @@ -228,7 +240,6 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches continue route_api = openapi["paths"][route] - method = list(route_api)[0] widget_id = ( ( route[1:].replace("/", "_") @@ -242,7 +253,7 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches if widget_id in widget_exclude_filter: continue - widget_config_dict = route_api[method].get("widget_config", {}) + widget_config_dict = route_api.get("get", {}).get("widget_config", {}) # If the widget is marked as excluded, skip it. if widget_config_dict.get("exclude") is True: @@ -250,10 +261,14 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches # Prepare the query schema of the widget query_schema, has_chart = get_query_schema_for_widget(openapi, route) - response_schema = route_api[method]["responses"]["200"]["content"][ - "application/json" - ].get("schema", {}) - + response_schema = ( + route_api.get("get", {}) + .get("responses", {}) + .get("200", {}) + .get("content", {}) + .get("application/json", {}) + .get("schema", {}) + ) # Extract providers from the query schema providers: list = [] for item in query_schema: @@ -264,8 +279,12 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches providers = ["custom"] for provider in providers: - columns_defs = data_schema_to_columns_defs( - openapi, widget_id, provider, route + + columns_defs = ( + data_schema_to_columns_defs(openapi, widget_id, provider, route) + if widget_config_dict.get("type") + not in ["multi_file_viewer", "pdf", "metric"] + else [] ) _cats = [ r @@ -294,8 +313,113 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches for word in name.split() ] ) + modified_query_schema = modify_query_schema(query_schema, provider) + if has_form_endpoint: + param_names: list = [] + var_schema: dict = {} + if ( + _schema := route_api.get("post", {}) + .get("requestBody", {}) + .get("content", {}) + .get("application/json", {}) + .get("schema", {}) + ): + schema_name = _schema.get("$ref", "").split("/")[-1] + var_schema = openapi["components"]["schemas"].get(schema_name, {}) + + if var_schema: + var_props = var_schema.get("properties", {}) + for k, v in var_props.items(): + if "$ref" in v: + param_names.append(k) + + if param_names: + for _param in param_names: + post_params = post_query_schema_for_widget( + openapi, form_route.get("operationId"), route, _param + ) + modified_post_params = modify_query_schema( + post_params, provider + ) + form_params = { + "type": "form", + "paramName": _param, + "label": "Form", + "description": "Form Data", + "endpoint": ( + route.replace("/api", "api") + if "/api" in route + else route[1:] if route[0] == "/" else route + ), + "inputParams": modified_post_params, + } + + if post_config := var_schema.get("x-widget_config", {}): + form_params = deep_merge_configs( + form_params, + post_config, + ) + + modified_query_schema.append(form_params) + else: + post_params = post_query_schema_for_widget( + openapi, form_route.get("operationId"), route + ) + modified_post_params = modify_query_schema(post_params, provider) + form_params = { + "type": "form", + "paramName": "", + "label": var_schema.get("title", "Form"), + "description": var_schema.get("description", ""), + "endpoint": ( + route.replace("/api", "api") + if "/api" in route + else route[1:] if route[0] == "/" else route + ), + "inputParams": modified_post_params, + } + + # Widget Config at the model level goes first. + if post_config := var_schema.get("x-widget_config", {}): + form_params = deep_merge_configs( + form_params, + post_config, + ) + + var_key: dict = {} + + # Then the widget config at the POST endpoint level takes priority. + if post_config := form_route.get("widget_config", {}): + for key, value in post_config.copy().items(): + if key.startswith("$."): + var_key[key] = value + + form_params = deep_merge_configs( + form_params, + { + k: v + for k, v in post_config.items() + if not k.startswith("$.") + }, + ) + + modified_query_schema.append(form_params) + + if var_key: + for key, value in var_key.items(): + if ( + key.replace("$.", "") in widget_config_dict + and "params" not in key + and "inputParams" not in key + ): + widget_config_dict.update( + {key.replace("$.", ""): value} + ) + else: + widget_config_dict[key.replace("$.", "")] = value + provider_map = { "tmx": "TMX", "ecb": "ECB", @@ -374,22 +498,22 @@ def build_json( # noqa: PLR0912 # pylint: disable=too-many-branches if columns_defs: widget_config["data"]["table"]["columnsDefs"] = columns_defs - var_key: dict = {} + data_var_key: dict = {} if data_config := data_schema_to_columns_defs( openapi, widget_id, provider, route, True ): for key, value in data_config.copy().items(): if key.startswith("$."): - var_key[key] = value + data_var_key[key] = value widget_config["data"] = deep_merge_configs( widget_config["data"], {k: v for k, v in data_config.items() if not k.startswith("$.")}, ) - if var_key: - for key, value in var_key.items(): + if data_var_key: + for key, value in data_var_key.items(): if ( key.replace("$.", "") in widget_config_dict and key != "$.data" diff --git a/openbb_platform/extensions/platform_api/tests/mock_openapi.json b/openbb_platform/extensions/platform_api/tests/mock_openapi.json index 00aca8cfdee..d4f709c886b 100644 --- a/openbb_platform/extensions/platform_api/tests/mock_openapi.json +++ b/openbb_platform/extensions/platform_api/tests/mock_openapi.json @@ -3500,6 +3500,87 @@ } ] } + }, + "/form_submit": { + "post": { + "summary": "Form Submit", + "operationId": "form_submit_form_submit_post", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Form" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Form Submit Form Submit Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "summary": "All Forms", + "operationId": "all_forms_form_submit_get", + "parameters": [ + { + "name": "some_param", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "", + "title": "Some Param" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + + }, + "title": "Response All Forms Form Submit Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } } }, "components": { @@ -6290,6 +6371,90 @@ "title": "SecSymbolMapData", "description": "SEC symbol map Data." }, + "Form": { + "properties": { + "date": { + "type": "string", + "format": "date", + "title": "Record Opened Date" + }, + "record_id": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "number" + } + ], + "title": "Record ID" + }, + "first_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "First Name" + }, + "last_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Last Name" + }, + "symptoms": { + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "title": "Symptoms", + "choices": [ + "dizzy", + "hungry", + "sleepy" + ], + "x-widget_config": { + "multiSelect": true + } + }, + "diagnosis": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Diagnosis" + }, + "add_record": { + "type": "button", + "title": "Add Record", + "default": true + } + }, + "additionalProperties": true, + "type": "object", + "title": "Submit Form", + "description": "Submit a form via POST request." + }, "ValidationError": { "properties": { "loc": { diff --git a/openbb_platform/extensions/platform_api/tests/mock_widgets.json b/openbb_platform/extensions/platform_api/tests/mock_widgets.json index d1a44be2620..f436a1c9874 100644 --- a/openbb_platform/extensions/platform_api/tests/mock_widgets.json +++ b/openbb_platform/extensions/platform_api/tests/mock_widgets.json @@ -11,8 +11,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -20,8 +20,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -29,8 +29,8 @@ "label": "Category", "description": "Category of survey response.", "optional": true, - "value": "spreads", "type": "text", + "value": "spreads", "show": true, "options": [ { @@ -80,6 +80,7 @@ "label": "Transform", "description": "Transformation type\n None = No transformation\n chg = Change\n ch1 = Change from Year Ago\n pch = Percent Change\n pc1 = Percent Change from Year Ago\n pca = Compounded Annual Rate of Change\n cch = Continuously Compounded Rate of Change\n cca = Continuously Compounded Annual Rate of Change\n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -116,7 +117,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -187,8 +187,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -196,8 +196,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -205,6 +205,7 @@ "label": "Frequency", "description": "Frequency aggregation to convert monthly data to lower frequency. None is monthly.", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -217,13 +218,13 @@ "value": "quarter" } ], - "type": "text", "paramName": "frequency" }, { "label": "Aggregation Method", "description": "A key that indicates the aggregation method used for frequency aggregation.\n \n avg = Average\n \n sum = Sum\n \n eop = End of Period", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -240,13 +241,13 @@ "value": "eop" } ], - "type": "text", "paramName": "aggregation_method" }, { "label": "Transform", "description": "Transformation type\n \n None = No transformation\n \n chg = Change\n \n ch1 = Change from Year Ago\n \n pch = Percent Change\n \n pc1 = Percent Change from Year Ago\n \n pca = Compounded Annual Rate of Change\n \n cch = Continuously Compounded Rate of Change\n \n cca = Continuously Compounded Annual Rate of Change\n \n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -283,7 +284,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -346,8 +346,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -355,8 +355,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -364,6 +364,7 @@ "label": "Frequency", "description": "Frequency aggregation to convert monthly data to lower frequency. None is monthly.", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -376,13 +377,13 @@ "value": "quarter" } ], - "type": "text", "paramName": "frequency" }, { "label": "Aggregation Method", "description": "A key that indicates the aggregation method used for frequency aggregation.\n \n avg = Average\n \n sum = Sum\n \n eop = End of Period", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -399,13 +400,13 @@ "value": "eop" } ], - "type": "text", "paramName": "aggregation_method" }, { "label": "Transform", "description": "Transformation type\n \n None = No transformation\n \n chg = Change\n \n ch1 = Change from Year Ago\n \n pch = Percent Change\n \n pc1 = Percent Change from Year Ago\n \n pca = Compounded Annual Rate of Change\n \n cch = Continuously Compounded Rate of Change\n \n cca = Continuously Compounded Annual Rate of Change\n \n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -442,7 +443,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -553,8 +553,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -562,8 +562,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -571,9 +571,9 @@ "label": "Topic", "description": "The topic for the survey response. Multiple comma separated items allowed.", "optional": true, + "type": "text", "value": "new_orders_growth", "show": true, - "type": "text", "multiSelect": true, "options": [ { @@ -639,6 +639,7 @@ "label": "Frequency", "description": "Frequency aggregation to convert monthly data to lower frequency. None is monthly.", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -651,13 +652,13 @@ "value": "quarter" } ], - "type": "text", "paramName": "frequency" }, { "label": "Aggregation Method", "description": "A key that indicates the aggregation method used for frequency aggregation.\n avg = Average\n sum = Sum\n eop = End of Period", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -674,13 +675,13 @@ "value": "eop" } ], - "type": "text", "paramName": "aggregation_method" }, { "label": "Transform", "description": "Transformation type\n None = No transformation\n chg = Change\n ch1 = Change from Year Ago\n pch = Percent Change\n pc1 = Percent Change from Year Ago\n pca = Compounded Annual Rate of Change\n cch = Continuously Compounded Rate of Change\n cca = Continuously Compounded Annual Rate of Change\n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -717,7 +718,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -803,8 +803,8 @@ "label": "Date", "description": "A specific date to get data for. Default is the latest report. Multiple comma separated items allowed.", "optional": true, - "value": null, "type": "text", + "value": null, "show": true, "multiSelect": true, "paramName": "date" @@ -813,8 +813,8 @@ "label": "Category", "description": "The category to query.", "optional": true, - "value": "employees_nsa", "type": "text", + "value": "employees_nsa", "show": true, "options": [ { @@ -984,8 +984,8 @@ "label": "Country", "description": "The country to get data. Multiple comma separated items allowed.", "optional": true, - "value": "united_states", "type": "text", + "value": "united_states", "show": true, "multiSelect": true, "options": [ @@ -1192,8 +1192,8 @@ "label": "Transform", "description": "Transformation of the CPI data. Period represents the change since previous. Defaults to change from one year ago (yoy).", "optional": true, - "value": "yoy", "type": "text", + "value": "yoy", "show": true, "options": [ { @@ -1215,8 +1215,8 @@ "label": "Frequency", "description": "The frequency of the data.", "optional": true, - "value": "monthly", "type": "text", + "value": "monthly", "show": true, "options": [ { @@ -1238,8 +1238,8 @@ "label": "Harmonized", "description": "If true, returns harmonized data.", "optional": true, - "value": false, "type": "boolean", + "value": false, "show": true, "paramName": "harmonized" }, @@ -1247,8 +1247,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -1256,8 +1256,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -1320,8 +1320,8 @@ "label": "Country", "description": "The country to get data. Enter as a 3-letter ISO country code, default is USA.", "optional": true, - "value": "united_states", "type": "text", + "value": "united_states", "show": true, "options": [ { @@ -1523,8 +1523,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -1532,8 +1532,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -1726,6 +1726,7 @@ "label": "Query", "description": "The search word(s).", "optional": true, + "type": "text", "value": null, "show": true, "paramName": "query" @@ -1734,8 +1735,8 @@ "label": "Is Release", "description": "Is release? If True, other search filter variables are ignored. If no query text or release_id is supplied, this defaults to True.", "optional": true, - "value": false, "type": "boolean", + "value": false, "show": true, "paramName": "is_release" }, @@ -1744,6 +1745,7 @@ "description": "A specific release ID to target.", "optional": true, "type": "text", + "value": null, "show": true, "paramName": "release_id" }, @@ -1751,8 +1753,8 @@ "label": "Limit", "description": "The number of data entries to return. (1-1000)", "optional": true, - "value": null, "type": "number", + "value": null, "show": true, "paramName": "limit" }, @@ -1760,8 +1762,8 @@ "label": "Offset", "description": "Offset the results in conjunction with limit.", "optional": true, - "value": 0, "type": "number", + "value": 0, "show": true, "paramName": "offset" }, @@ -1769,6 +1771,7 @@ "label": "Filter Variable", "description": "Filter by an attribute.", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -1785,13 +1788,13 @@ "value": "seasonal_adjustment" } ], - "type": "text", "paramName": "filter_variable" }, { "label": "Filter Value", "description": "String value to filter the variable by. Used in conjunction with filter_variable.", "optional": true, + "type": "text", "value": null, "show": true, "paramName": "filter_value" @@ -1800,9 +1803,9 @@ "label": "Tag Names", "description": "A semicolon delimited list of tag names that series match all of. Example: 'japan;imports' Multiple comma separated items allowed.", "optional": true, + "type": "text", "value": null, "show": true, - "type": "text", "multiSelect": true, "paramName": "tag_names" }, @@ -1810,9 +1813,9 @@ "label": "Exclude Tag Names", "description": "A semicolon delimited list of tag names that series match none of. Example: 'imports;services'. Requires that variable tag_names also be set to limit the number of matching series. Multiple comma separated items allowed.", "optional": true, + "type": "text", "value": null, "show": true, - "type": "text", "multiSelect": true, "paramName": "exclude_tag_names" }, @@ -1821,6 +1824,7 @@ "description": "A FRED Series ID to return series group information for. This returns the required information to query for regional data. Not all series that are in FRED have geographical data. Entering a value for series_id will override all other parameters. Multiple series_ids can be separated by commas.", "optional": true, "type": "text", + "value": null, "show": true, "paramName": "series_id" }, @@ -2005,6 +2009,7 @@ "description": "Symbol to get data for. Multiple comma separated items allowed.", "optional": false, "type": "text", + "value": null, "show": true, "multiSelect": true, "paramName": "symbol" @@ -2013,8 +2018,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -2022,8 +2027,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -2031,8 +2036,8 @@ "label": "Limit", "description": "The number of data entries to return.", "optional": true, - "value": 100000, "type": "number", + "value": 100000, "show": true, "paramName": "limit" }, @@ -2040,6 +2045,7 @@ "label": "Frequency", "description": "Frequency aggregation to convert high frequency data to lower frequency.\n \n None = No change\n \n a = Annual\n \n q = Quarterly\n \n m = Monthly\n \n w = Weekly\n \n d = Daily\n \n wef = Weekly, Ending Friday\n \n weth = Weekly, Ending Thursday\n \n wew = Weekly, Ending Wednesday\n \n wetu = Weekly, Ending Tuesday\n \n wem = Weekly, Ending Monday\n \n wesu = Weekly, Ending Sunday\n \n wesa = Weekly, Ending Saturday\n \n bwew = Biweekly, Ending Wednesday\n \n bwem = Biweekly, Ending Monday", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2100,13 +2106,13 @@ "value": "bwem" } ], - "type": "text", "paramName": "frequency" }, { "label": "Aggregation Method", "description": "A key that indicates the aggregation method used for frequency aggregation.\n This parameter has no affect if the frequency parameter is not set.\n \n avg = Average\n \n sum = Sum\n \n eop = End of Period", "optional": true, + "type": "text", "value": "eop", "show": true, "options": [ @@ -2123,13 +2129,13 @@ "value": "eop" } ], - "type": "text", "paramName": "aggregation_method" }, { "label": "Transform", "description": "Transformation type\n \n None = No transformation\n \n chg = Change\n \n ch1 = Change from Year Ago\n \n pch = Percent Change\n \n pc1 = Percent Change from Year Ago\n \n pca = Compounded Annual Rate of Change\n \n cch = Continuously Compounded Rate of Change\n \n cca = Continuously Compounded Annual Rate of Change\n \n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2166,7 +2172,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -2215,6 +2220,7 @@ "description": "Symbol to get data for. Multiple comma separated items allowed.", "optional": false, "type": "text", + "value": null, "show": true, "multiSelect": true, "paramName": "symbol" @@ -2223,8 +2229,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -2232,8 +2238,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -2241,8 +2247,8 @@ "label": "Limit", "description": "The number of data entries to return.", "optional": true, - "value": 100000, "type": "number", + "value": 100000, "show": true, "paramName": "limit" }, @@ -2250,6 +2256,7 @@ "label": "Frequency", "description": "Frequency aggregation to convert high frequency data to lower frequency.\n \n None = No change\n \n a = Annual\n \n q = Quarterly\n \n m = Monthly\n \n w = Weekly\n \n d = Daily\n \n wef = Weekly, Ending Friday\n \n weth = Weekly, Ending Thursday\n \n wew = Weekly, Ending Wednesday\n \n wetu = Weekly, Ending Tuesday\n \n wem = Weekly, Ending Monday\n \n wesu = Weekly, Ending Sunday\n \n wesa = Weekly, Ending Saturday\n \n bwew = Biweekly, Ending Wednesday\n \n bwem = Biweekly, Ending Monday", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2310,13 +2317,13 @@ "value": "bwem" } ], - "type": "text", "paramName": "frequency" }, { "label": "Aggregation Method", "description": "A key that indicates the aggregation method used for frequency aggregation.\n This parameter has no affect if the frequency parameter is not set.\n \n avg = Average\n \n sum = Sum\n \n eop = End of Period", "optional": true, + "type": "text", "value": "eop", "show": true, "options": [ @@ -2333,13 +2340,13 @@ "value": "eop" } ], - "type": "text", "paramName": "aggregation_method" }, { "label": "Transform", "description": "Transformation type\n \n None = No transformation\n \n chg = Change\n \n ch1 = Change from Year Ago\n \n pch = Percent Change\n \n pc1 = Percent Change from Year Ago\n \n pca = Compounded Annual Rate of Change\n \n cch = Continuously Compounded Rate of Change\n \n cca = Continuously Compounded Annual Rate of Change\n \n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2376,7 +2383,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -2435,6 +2441,7 @@ "description": "The ID of the release. Use `fred_search` to find releases.", "optional": false, "type": "text", + "value": null, "show": true, "paramName": "release_id" }, @@ -2442,6 +2449,7 @@ "label": "Element ID", "description": "The element ID of a specific table in the release.", "optional": true, + "type": "text", "value": null, "show": true, "paramName": "element_id" @@ -2450,8 +2458,8 @@ "label": "Date", "description": "A specific date to get data for. Multiple comma separated items allowed.", "optional": true, - "value": null, "type": "text", + "value": null, "show": true, "multiSelect": true, "paramName": "date" @@ -2567,6 +2575,7 @@ "description": "Symbol to get data for.", "optional": false, "type": "text", + "value": null, "show": true, "paramName": "symbol" }, @@ -2574,8 +2583,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -2583,8 +2592,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -2592,8 +2601,8 @@ "label": "Limit", "description": "The number of data entries to return.", "optional": true, - "value": 100000, "type": "number", + "value": 100000, "show": true, "paramName": "limit" }, @@ -2601,8 +2610,8 @@ "label": "Is Series Group", "description": "When True, the symbol provided is for a series_group, else it is for a series ID.", "optional": true, - "value": false, "type": "boolean", + "value": false, "show": true, "paramName": "is_series_group" }, @@ -2610,6 +2619,7 @@ "label": "Region Type", "description": "The type of regional data. Parameter is only valid when `is_series_group` is True.", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2646,15 +2656,14 @@ "value": "censusregion" } ], - "type": "text", "paramName": "region_type" }, { "label": "Season", "description": "The seasonal adjustments to the data. Parameter is only valid when `is_series_group` is True.", "optional": true, - "value": "nsa", "type": "text", + "value": "nsa", "show": true, "options": [ { @@ -2676,6 +2685,7 @@ "label": "Units", "description": "The units of the data. This should match the units returned from searching by series ID. An incorrect field will not necessarily return an error. Parameter is only valid when `is_series_group` is True.", "optional": true, + "type": "text", "value": null, "show": true, "paramName": "units" @@ -2684,6 +2694,7 @@ "label": "Frequency", "description": "Frequency aggregation to convert high frequency data to lower frequency.\n \n None = No change\n \n a = Annual\n \n q = Quarterly\n \n m = Monthly\n \n w = Weekly\n \n d = Daily\n \n wef = Weekly, Ending Friday\n \n weth = Weekly, Ending Thursday\n \n wew = Weekly, Ending Wednesday\n \n wetu = Weekly, Ending Tuesday\n \n wem = Weekly, Ending Monday\n \n wesu = Weekly, Ending Sunday\n \n wesa = Weekly, Ending Saturday\n \n bwew = Biweekly, Ending Wednesday\n \n bwem = Biweekly, Ending Monday", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2744,13 +2755,13 @@ "value": "bwem" } ], - "type": "text", "paramName": "frequency" }, { "label": "Aggregation Method", "description": "A key that indicates the aggregation method used for frequency aggregation.\n This parameter has no affect if the frequency parameter is not set.\n \n avg = Average\n \n sum = Sum\n \n eop = End of Period", "optional": true, + "type": "text", "value": "eop", "show": true, "options": [ @@ -2767,13 +2778,13 @@ "value": "eop" } ], - "type": "text", "paramName": "aggregation_method" }, { "label": "Transform", "description": "Transformation type\n \n None = No transformation\n \n chg = Change\n \n ch1 = Change from Year Ago\n \n pch = Percent Change\n \n pc1 = Percent Change from Year Ago\n \n pca = Compounded Annual Rate of Change\n \n cch = Continuously Compounded Rate of Change\n \n cca = Continuously Compounded Annual Rate of Change\n \n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -2810,7 +2821,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -2887,6 +2897,7 @@ "label": "Item", "description": "The item or basket of items to query.", "optional": true, + "type": "text", "value": null, "show": true, "paramName": "item" @@ -2895,8 +2906,8 @@ "label": "Country", "description": "The country to get data.", "optional": true, - "value": "united_states", "type": "text", + "value": "united_states", "show": true, "paramName": "country" }, @@ -2904,8 +2915,8 @@ "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "start_date" }, @@ -2913,8 +2924,8 @@ "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": true, - "value": null, "type": "date", + "value": null, "show": true, "paramName": "end_date" }, @@ -2922,8 +2933,8 @@ "label": "Region", "description": "The region to get average price levels for.", "optional": true, - "value": "all_city", "type": "text", + "value": "all_city", "show": true, "options": [ { @@ -2953,8 +2964,8 @@ "label": "Frequency", "description": "The frequency of the data.", "optional": true, - "value": "monthly", "type": "text", + "value": "monthly", "show": true, "options": [ { @@ -2976,6 +2987,7 @@ "label": "Transform", "description": "Transformation type\n None = No transformation\n chg = Change\n ch1 = Change from Year Ago\n pch = Percent Change\n pc1 = Percent Change from Year Ago\n pca = Compounded Annual Rate of Change\n cch = Continuously Compounded Rate of Change\n cca = Continuously Compounded Annual Rate of Change\n log = Natural Log", "optional": true, + "type": "text", "value": null, "show": true, "options": [ @@ -3012,7 +3024,6 @@ "value": "log" } ], - "type": "text", "paramName": "transform" }, { @@ -3089,8 +3100,8 @@ "label": "Date", "description": "A specific date to get data for. Default is the latest report. Multiple comma separated items allowed.", "optional": true, - "value": null, "type": "text", + "value": null, "show": true, "multiSelect": true, "paramName": "date" @@ -3099,8 +3110,8 @@ "label": "Category", "description": "The category to query.", "optional": true, - "value": "personal_income", "type": "text", + "value": "personal_income", "show": true, "options": [ { @@ -3242,6 +3253,7 @@ "description": "Symbol to get data for.", "optional": false, "type": "text", + "value": null, "show": true, "paramName": "symbol" }, @@ -3249,8 +3261,8 @@ "label": "Use Cache", "description": "Whether or not to use cache for the request, default is True.", "optional": true, - "value": true, "type": "boolean", + "value": true, "show": true, "paramName": "use_cache" }, @@ -3298,8 +3310,8 @@ "label": "Query", "description": "Search query.", "optional": true, - "value": "", "type": "text", + "value": "", "show": true, "paramName": "query" }, @@ -3307,8 +3319,8 @@ "label": "Use Cache", "description": "Whether or not to use cache.", "optional": true, - "value": true, "type": "boolean", + "value": true, "show": true, "paramName": "use_cache" }, @@ -3363,8 +3375,8 @@ "label": "Query", "description": "Search query.", "optional": true, - "value": "", "type": "text", + "value": "", "show": true, "paramName": "query" }, @@ -3372,8 +3384,8 @@ "label": "Use Cache", "description": "Whether or not to use cache.", "optional": true, - "value": true, "type": "boolean", + "value": true, "show": true, "paramName": "use_cache" }, @@ -3381,6 +3393,7 @@ "label": "URL", "description": "Enter an optional URL path to fetch the next level.", "optional": true, + "type": "text", "value": null, "show": true, "paramName": "url" @@ -3429,8 +3442,8 @@ "label": "Query", "description": "Search query.", "optional": false, - "value": null, "type": "text", + "value": null, "show": true, "paramName": "query" }, @@ -3438,8 +3451,8 @@ "label": "Use Cache", "description": "Whether or not to use cache. If True, cache will store for seven days.", "optional": true, - "value": true, "type": "boolean", + "value": true, "show": true, "paramName": "use_cache" }, @@ -3558,8 +3571,8 @@ "label": "Query", "description": "Search query.", "optional": true, - "value": "", "type": "text", + "value": "", "show": true, "paramName": "query" }, @@ -3567,8 +3580,8 @@ "label": "Use Cache", "description": "Whether or not to use cache.", "optional": true, - "value": true, "type": "boolean", + "value": true, "show": true, "paramName": "use_cache" }, @@ -3617,5 +3630,119 @@ "SEC" ], "subCategory": "SEC" + }, + "form_submit_custom_obb": { + "name": "Form Submit", + "description": "", + "category": "Form Submit", + "type": "table", + "searchCategory": "Form Submit", + "widgetId": "form_submit_custom_obb", + "params": [ + { + "label": "Some Param", + "description": "", + "optional": true, + "type": "text", + "value": "", + "show": true, + "paramName": "some_param" + }, + { + "type": "form", + "paramName": "", + "label": "Submit Form", + "description": "Submit a form via POST request.", + "endpoint": "form_submit", + "inputParams": [ + { + "label": "Record Opened Date", + "description": "", + "optional": true, + "type": "date", + "value": null, + "paramName": "date" + }, + { + "label": "Record ID", + "description": "", + "optional": true, + "type": "number", + "value": null, + "paramName": "record_id" + }, + { + "label": "First Name", + "description": "", + "optional": true, + "type": "text", + "value": null, + "paramName": "first_name" + }, + { + "label": "Last Name", + "description": "", + "optional": true, + "type": "text", + "value": null, + "paramName": "last_name" + }, + { + "label": "Symptoms", + "description": "", + "optional": true, + "type": "text", + "value": null, + "multiSelect": true, + "options": [ + { + "label": "dizzy", + "value": "dizzy" + }, + { + "label": "hungry", + "value": "hungry" + }, + { + "label": "sleepy", + "value": "sleepy" + } + ], + "paramName": "symptoms" + }, + { + "label": "Diagnosis", + "description": "", + "optional": true, + "type": "text", + "value": null, + "paramName": "diagnosis" + }, + { + "label": "Add Record", + "description": "", + "optional": true, + "type": "button", + "value": true, + "paramName": "add_record" + } + ] + } + ], + "endpoint": "form_submit", + "runButton": false, + "gridData": { + "w": 40, + "h": 15 + }, + "data": { + "dataKey": "", + "table": { + "showAll": true + } + }, + "source": [ + "Custom" + ] } } \ No newline at end of file diff --git a/openbb_platform/extensions/platform_api/tests/test_openapi_utils.py b/openbb_platform/extensions/platform_api/tests/test_openapi_utils.py index 6be35d48852..aa9806cc97e 100644 --- a/openbb_platform/extensions/platform_api/tests/test_openapi_utils.py +++ b/openbb_platform/extensions/platform_api/tests/test_openapi_utils.py @@ -6,6 +6,7 @@ from openbb_platform_api.utils.openapi import ( data_schema_to_columns_defs, get_data_schema_for_widget, get_query_schema_for_widget, + post_query_schema_for_widget, ) # pylint: disable=redefined-outer-name @@ -32,6 +33,7 @@ def mock_openapi_json(): "description": "Source of the data.", "optional": True, "type": "text", + "value": None, "available_providers": ["fred"], "show": True, }, @@ -40,7 +42,9 @@ def mock_openapi_json(): "label": "Country", "description": "The country to get data. Enter as a 3-letter ISO country code, default is USA.", "optional": True, + "type": "text", "value": "united_states", + "multiple_items_allowed": {}, "options": { "fred": [ {"label": "argentina", "value": "argentina"}, @@ -93,37 +97,32 @@ def mock_openapi_json(): {"label": "g20", "value": "g20"}, ] }, - "multiple_items_allowed": {}, - "available_providers": ["fred"], - "type": "text", - "show": True, "x-widget_config": {}, + "show": True, }, { "parameter_name": "start_date", "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": True, - "value": None, - "options": {"fred": []}, - "multiple_items_allowed": {}, - "available_providers": ["fred"], "type": "date", - "show": True, + "value": None, + "multiple_items_allowed": {}, + "options": {"fred": []}, "x-widget_config": {}, + "show": True, }, { "parameter_name": "end_date", "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": True, - "value": None, - "options": {"fred": []}, - "multiple_items_allowed": {}, - "available_providers": ["fred"], "type": "date", - "show": True, + "value": None, + "multiple_items_allowed": {}, + "options": {"fred": []}, "x-widget_config": {}, + "show": True, }, ], False, @@ -138,6 +137,7 @@ def mock_openapi_json(): "description": "Source of the data.", "optional": True, "type": "text", + "value": None, "available_providers": ["fred"], "show": True, }, @@ -147,6 +147,7 @@ def mock_openapi_json(): "description": "Symbol to get data for.", "optional": False, "type": "text", + "value": None, "multiple_items_allowed": {"fred": True}, "show": True, }, @@ -155,71 +156,45 @@ def mock_openapi_json(): "label": "Start Date", "description": "Start date of the data, in YYYY-MM-DD format.", "optional": True, - "value": None, - "options": {"fred": []}, - "multiple_items_allowed": {}, - "available_providers": ["fred"], "type": "date", - "show": True, + "value": None, + "multiple_items_allowed": {}, + "options": {"fred": []}, "x-widget_config": {}, + "show": True, }, { "parameter_name": "end_date", "label": "End Date", "description": "End date of the data, in YYYY-MM-DD format.", "optional": True, - "value": None, - "options": {"fred": []}, - "multiple_items_allowed": {}, - "available_providers": ["fred"], "type": "date", - "show": True, + "value": None, + "multiple_items_allowed": {}, + "options": {"fred": []}, "x-widget_config": {}, + "show": True, }, { "parameter_name": "limit", "label": "Limit", "description": "The number of data entries to return.", "optional": True, - "value": 100000, - "options": {"fred": []}, - "multiple_items_allowed": {}, - "available_providers": ["fred"], "type": "number", - "show": True, + "value": 100000, + "multiple_items_allowed": {}, + "options": {"fred": []}, "x-widget_config": {}, + "show": True, }, { "parameter_name": "frequency", "label": "Frequency", - "description": "Frequency aggregation to convert high frequency data to lower frequency." - + "\n " - + "\n None = No change" - + "\n " - + "\n a = Annual\n " - + "\n q = Quarterly\n " - + "\n m = Monthly\n " - + "\n w = Weekly\n " - + "\n d = Daily\n " - + "\n wef = Weekly, Ending Friday" - + "\n " - + "\n weth = Weekly, Ending Thursday" - + "\n " - + "\n wew = Weekly, Ending Wednesday" - + "\n " - + "\n wetu = Weekly, Ending Tuesday" - + "\n " - + "\n wem = Weekly, Ending Monday" - + "\n " - + "\n wesu = Weekly, Ending Sunday" - + "\n " - + "\n wesa = Weekly, Ending Saturday" - + "\n " - + "\n bwew = Biweekly, Ending Wednesday" - + "\n " - + "\n bwem = Biweekly, Ending Monday", + "description": "Frequency aggregation to convert high frequency data to lower frequency.\n \n None = No change\n \n a = Annual\n \n q = Quarterly\n \n m = Monthly\n \n w = Weekly\n \n d = Daily\n \n wef = Weekly, Ending Friday\n \n weth = Weekly, Ending Thursday\n \n wew = Weekly, Ending Wednesday\n \n wetu = Weekly, Ending Tuesday\n \n wem = Weekly, Ending Monday\n \n wesu = Weekly, Ending Sunday\n \n wesa = Weekly, Ending Saturday\n \n bwew = Biweekly, Ending Wednesday\n \n bwem = Biweekly, Ending Monday", "optional": True, + "type": "text", "value": None, + "multiple_items_allowed": {}, "options": { "fred": [ {"label": "a", "value": "a"}, @@ -238,24 +213,17 @@ def mock_openapi_json(): {"label": "bwem", "value": "bwem"}, ] }, - "multiple_items_allowed": {}, - "available_providers": ["fred"], - "show": True, "x-widget_config": {}, + "show": True, }, { "parameter_name": "aggregation_method", "label": "Aggregation Method", - "description": "A key that indicates the aggregation method used for frequency aggregation." - + "\n This parameter has no affect if the frequency parameter is not set." - + "\n " - + "\n avg = Average" - + "\n " - + "\n sum = Sum" - + "\n " - + "\n eop = End of Period", + "description": "A key that indicates the aggregation method used for frequency aggregation.\n This parameter has no affect if the frequency parameter is not set.\n \n avg = Average\n \n sum = Sum\n \n eop = End of Period", "optional": True, + "type": "text", "value": "eop", + "multiple_items_allowed": {}, "options": { "fred": [ {"label": "avg", "value": "avg"}, @@ -263,35 +231,17 @@ def mock_openapi_json(): {"label": "eop", "value": "eop"}, ] }, - "multiple_items_allowed": {}, - "available_providers": ["fred"], - "show": True, "x-widget_config": {}, + "show": True, }, { "parameter_name": "transform", "label": "Transform", - "description": "Transformation type" - + "\n " - + "\n None = No transformation" - + "\n " - + "\n chg = Change" - + "\n " - + "\n ch1 = Change from Year Ago" - + "\n " - + "\n pch = Percent Change" - + "\n " - + "\n pc1 = Percent Change from Year Ago" - + "\n " - + "\n pca = Compounded Annual Rate of Change" - + "\n " - + "\n cch = Continuously Compounded Rate of Change" - + "\n " - + "\n cca = Continuously Compounded Annual Rate of Change" - + "\n " - + "\n log = Natural Log", + "description": "Transformation type\n \n None = No transformation\n \n chg = Change\n \n ch1 = Change from Year Ago\n \n pch = Percent Change\n \n pc1 = Percent Change from Year Ago\n \n pca = Compounded Annual Rate of Change\n \n cch = Continuously Compounded Rate of Change\n \n cca = Continuously Compounded Annual Rate of Change\n \n log = Natural Log", "optional": True, + "type": "text", "value": None, + "multiple_items_allowed": {}, "options": { "fred": [ {"label": "chg", "value": "chg"}, @@ -304,10 +254,8 @@ def mock_openapi_json(): {"label": "log", "value": "log"}, ] }, - "multiple_items_allowed": {}, - "available_providers": ["fred"], - "show": True, "x-widget_config": {}, + "show": True, }, ], True, @@ -322,6 +270,7 @@ def mock_openapi_json(): "description": "Source of the data.", "optional": True, "type": "text", + "value": None, "available_providers": ["sec"], "show": True, }, @@ -330,24 +279,22 @@ def mock_openapi_json(): "label": "Query", "description": "Search query.", "optional": True, - "value": "", - "options": {"sec": []}, - "multiple_items_allowed": {}, - "available_providers": ["sec"], "type": "text", - "show": True, + "value": "", + "multiple_items_allowed": {}, + "options": {"sec": []}, "x-widget_config": {}, + "show": True, }, { "parameter_name": "use_cache", "label": "Use Cache", "description": "Whether or not to use cache.", "optional": True, - "value": True, - "options": {"sec": []}, - "multiple_items_allowed": {}, - "available_providers": ["sec"], "type": "boolean", + "value": True, + "multiple_items_allowed": {}, + "options": {"sec": []}, "x-widget_config": {}, "show": True, }, @@ -356,10 +303,10 @@ def mock_openapi_json(): "label": "Url", "description": "Enter an optional URL path to fetch the next level.", "optional": True, + "type": "text", "value": None, - "options": {"sec": []}, "multiple_items_allowed": {}, - "available_providers": ["sec"], + "options": {"sec": []}, "x-widget_config": {}, "show": True, }, @@ -390,6 +337,17 @@ def test_get_data_schema_for_widget(mock_openapi_json, openapi_operation_id): assert schema is not None +@pytest.mark.parametrize( + "openapi_operation_id", + [ + "form_submit_form_submit_post", + ], +) +def test_post_query_schema_for_widget(mock_openapi_json, openapi_operation_id): + schema = post_query_schema_for_widget(mock_openapi_json, openapi_operation_id) + assert schema + + @pytest.mark.parametrize( "openapi_operation_id", [