Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Enhance actions REST API #4392

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

lolodomo
Copy link
Contributor

@lolodomo lolodomo commented Sep 28, 2024

API GET /actions/{thingUID} now returns the input parameters also as a list of configuration description parameters.
It is provided only when all input parameters have a type than can be mapped to the type of a configuration description parameter.
It will be used in particular by Main UI to expose actions.

Also enhance the POST API (execute a thing action) in order to be more flexible regarding the type of each provided argument value and to map the value to the expected data type.

Related to #1745

Signed-off-by: Laurent Garnier [email protected]

@lolodomo lolodomo requested a review from a team as a code owner September 28, 2024 17:04
@lolodomo lolodomo marked this pull request as draft September 28, 2024 17:04
@lolodomo
Copy link
Contributor Author

For the following action

    @RuleAction(label = "test", description = "Test action")
    public void testAction(
            @ActionInput(name = "booleanParam", label = "boolean parameter", required = true, description = "Descr boolean parameter") boolean booleanParam,
            @ActionInput(name = "booleanParam1", label = "Boolean param 1", required = false, description = "Descr Boolean param 1") @Nullable Boolean booleanParam1,
            @ActionInput(name = "booleanParam2", label = "Boolean param 2", required = true, description = "Descr Boolean param 2") Boolean booleanParam2,
            @ActionInput(name = "intParam", label = "int parameter", required = true, description = "Descr int parameter") int intParam,
            @ActionInput(name = "IntegerParam1", label = "Integer param 1", required = false, description = "Descr Integer param 1") @Nullable Integer IntegerParam1,
            @ActionInput(name = "IntegerParam2", label = "Integer param 2", required = true, description = "Descr Integer param 2") Integer IntegerParam2,
            @ActionInput(name = "longParam", label = "long parameter", required = true, description = "Descr long parameter") long longParam,
            @ActionInput(name = "longParam1", label = "Long param 1", required = false, description = "Descr Long param 1") @Nullable Long longParam1,
            @ActionInput(name = "longParam2", label = "Long param 2", required = true, description = "Descr Long param 2") Long longParam2,
            @ActionInput(name = "doubleParam", label = "double parameter", required = true, description = "Descr double parameter") double doubleParam,
            @ActionInput(name = "doubleParam1", label = "Double param 1", required = false, description = "Descr Double param 1") @Nullable Double doubleParam1,
            @ActionInput(name = "doubleParam2", label = "Double param 2", required = true, description = "Descr Double param 2") Double doubleParam2,
            @ActionInput(name = "stringParam1", label = "String param 1", required = false, description = "Descr String param 1") @Nullable String stringParam1,
            @ActionInput(name = "stringParam2", label = "String param 2", required = true, description = "Descr String param 2") String stringParam2,
            @ActionInput(name = "stringParam3", label = "String param 3", description = "Descr String param 3") @Nullable String stringParam3,
            @ActionInput(name = "stringParam4", label = "String param 4", description = "Descr String param 4") String stringParam4) {
    }

The API now returns:

[
  {
    "actionUid": "astro.testAction",
    "label": "test",
    "description": "Test action",
    "inputs": [
      {
        "name": "booleanParam",
        "type": "boolean",
        "label": "boolean parameter",
        "description": "Descr boolean parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "booleanParam1",
        "type": "java.lang.Boolean",
        "label": "Boolean param 1",
        "description": "Descr Boolean param 1",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "booleanParam2",
        "type": "java.lang.Boolean",
        "label": "Boolean param 2",
        "description": "Descr Boolean param 2",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "intParam",
        "type": "int",
        "label": "int parameter",
        "description": "Descr int parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "IntegerParam1",
        "type": "java.lang.Integer",
        "label": "Integer param 1",
        "description": "Descr Integer param 1",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "IntegerParam2",
        "type": "java.lang.Integer",
        "label": "Integer param 2",
        "description": "Descr Integer param 2",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "longParam",
        "type": "long",
        "label": "long parameter",
        "description": "Descr long parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "longParam1",
        "type": "java.lang.Long",
        "label": "Long param 1",
        "description": "Descr Long param 1",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "longParam2",
        "type": "java.lang.Long",
        "label": "Long param 2",
        "description": "Descr Long param 2",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "doubleParam",
        "type": "double",
        "label": "double parameter",
        "description": "Descr double parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "doubleParam1",
        "type": "java.lang.Double",
        "label": "Double param 1",
        "description": "Descr Double param 1",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "doubleParam2",
        "type": "java.lang.Double",
        "label": "Double param 2",
        "description": "Descr Double param 2",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "stringParam1",
        "type": "java.lang.String",
        "label": "String param 1",
        "description": "Descr String param 1",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "stringParam2",
        "type": "java.lang.String",
        "label": "String param 2",
        "description": "Descr String param 2",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "stringParam3",
        "type": "java.lang.String",
        "label": "String param 3",
        "description": "Descr String param 3",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "stringParam4",
        "type": "java.lang.String",
        "label": "String param 4",
        "description": "Descr String param 4",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      }
    ],
    "inputsAsConfigParameters": [
      {
        "default": "false",
        "description": "Descr boolean parameter",
        "label": "boolean parameter",
        "name": "booleanParam",
        "required": true,
        "type": "BOOLEAN",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Boolean param 1",
        "label": "Boolean param 1",
        "name": "booleanParam1",
        "required": false,
        "type": "BOOLEAN",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Boolean param 2",
        "label": "Boolean param 2",
        "name": "booleanParam2",
        "required": true,
        "type": "BOOLEAN",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "default": "0",
        "description": "Descr int parameter",
        "label": "int parameter",
        "name": "intParam",
        "required": true,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Integer param 1",
        "label": "Integer param 1",
        "name": "IntegerParam1",
        "required": false,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Integer param 2",
        "label": "Integer param 2",
        "name": "IntegerParam2",
        "required": true,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "default": "0",
        "description": "Descr long parameter",
        "label": "long parameter",
        "name": "longParam",
        "required": true,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Long param 1",
        "label": "Long param 1",
        "name": "longParam1",
        "required": false,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Long param 2",
        "label": "Long param 2",
        "name": "longParam2",
        "required": true,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "default": "0",
        "description": "Descr double parameter",
        "label": "double parameter",
        "name": "doubleParam",
        "required": true,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Double param 1",
        "label": "Double param 1",
        "name": "doubleParam1",
        "required": false,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Double param 2",
        "label": "Double param 2",
        "name": "doubleParam2",
        "required": true,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr String param 1",
        "label": "String param 1",
        "name": "stringParam1",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr String param 2",
        "label": "String param 2",
        "name": "stringParam2",
        "required": true,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr String param 3",
        "label": "String param 3",
        "name": "stringParam3",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr String param 4",
        "label": "String param 4",
        "name": "stringParam4",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      }
    ],
    "outputs": []
  }
]

ThingActionDTO actionDTO = new ThingActionDTO();
actionDTO.actionUid = actionType.getUID();
actionDTO.description = actionType.getDescription();
actionDTO.label = actionType.getLabel();
actionDTO.inputs = actionType.getInputs();
actionDTO.inputsAsConfigParameters = inputParameters == null ? null
Copy link
Contributor

Choose a reason for hiding this comment

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

I would rather name it:

Suggested change
actionDTO.inputsAsConfigParameters = inputParameters == null ? null
actionDTO.inputConfigDescriptions = inputParameters == null ? null

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok if you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for picking this naming topic up again, but would it make sense to use the singular?
inputConfigDescription?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would rather suggest inputsConfigDescription. The plural will indicate we have a list of something.

Or maybe configDescriptionInputs ?

break;
case "java.time.LocalDate":
parameterType = ConfigDescriptionParameter.Type.TEXT;
context = "date";
Copy link
Contributor

Choose a reason for hiding this comment

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

There is AFAIK no date context in the UI, but would probably nice to add one.

Copy link
Contributor Author

@lolodomo lolodomo Sep 29, 2024

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the link … if it is not in the UI I will add it.
That’s however nothing that matters for this PR, I was just noting that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am curious to check if this is already used by any binding.

We could also check if this "context" is documented in our documentation and not only in Java doc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

time as context value is used by 4 bindings:

$ find */src -name "*.xml" -exec grep -H "<context>" {} \; | grep ime
org.openhab.binding.astro/src/main/resources/OH-INF/config/config.xml:                  <context>time</context>
org.openhab.binding.astro/src/main/resources/OH-INF/config/config.xml:                  <context>time</context>
org.openhab.binding.gpio/src/main/resources/OH-INF/thing/pigpio-remote.xml:                             <context>time</context>
org.openhab.binding.gpio/src/main/resources/OH-INF/thing/pigpio-remote.xml:                             <context>time</context>
org.openhab.binding.lametrictime/src/main/resources/OH-INF/thing/device.xml:                            <context>password</context>
org.openhab.binding.robonect/src/main/resources/OH-INF/config/config.xml:                       <context>time</context>
org.openhab.transform.basicprofiles/src/main/resources/OH-INF/config/time-range-command.xml:                    <context>time</context>
org.openhab.transform.basicprofiles/src/main/resources/OH-INF/config/time-range-command.xml:                    <context>time</context>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And datetime or date is apparently not yet used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So it would be important that you support date and datetime contexts mainly for thing actions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is already properly documented:
https://www.openhab.org/docs/developer/addons/config-xml.html#supported-contexts

I have to check if formats mentioned in doc match the ISO formats because you will certainly use the documented formats when calling the action. So we have to support such string formats as input.

@lolodomo lolodomo changed the title [WIP] Enhance API GET /actions/{thingUID} to return input params as config … Enhance actions REST API Sep 29, 2024
API GET /actions/{thingUID} now returns the input parameters also as a list of configuration description parameters.
It is provided only when all input parameters have a type than can be mapped to the type of a configuration description parameter.
It will be used in particular by Main UI to expose actions.

Also enhance the POST API (execute a thing action) in order to be more flexible regarding the type of each provided argument value and to map the value to the expected data type.

Related to openhab#1745

Signed-off-by: Laurent Garnier <[email protected]>
@florian-h05
Copy link
Contributor

florian-h05 commented Sep 30, 2024

@lolodomo The /rest/module-types endpoint also needs to provide these config descriptions for Thing action modules, so Thing actions can be invoked from rules.

@lolodomo
Copy link
Contributor Author

I don't understand, Thing actions can already be invoked from rules. ???

@florian-h05
Copy link
Contributor

After reading #1745 completely, yes they could in theory - the problem is however the same we attempt to fix with this PR: The Thing action module types only contain the inputs like the /actions endpoint, which makes it impossible for the UI to allow the user to provide the required inputs.

@florian-h05
Copy link
Contributor

@lolodomo Please do not force push from now on - I will start working on your PR as well and create a PR to your PR branch afterwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants