HTTP Server config

Hi @Rich, @Don @michal . I was wondering if you’d had a chance to check out the post? Is there a way to update multiple parameters of an asset? I tried, using PUT and adjusting the REST URL but as you can see in the previous post, I get a 403 Forbidden.

Your input would be greatly appreciated!

Hi,

I have just added an endpoint to the asset REST API so you can PUT multiple attributes in one call (any manager docker image with a created date after this post will have this functionality in it):

PUT: /api/{realm}/asset/attributes (Payload = AttributeState[])

Payload example for 2 attributes, one called notes and the other roomNumber:

[
  {
    "ref": {
      "id": "60hocrKYlcvseEqn6vGZlu",
      "name": "notes"
    },
    "value": "test"
  },
  {
    "ref": {
      "id": "60hocrKYlcvseEqn6vGZlu",
      "name": "roomNumber"
    },
    "value": 123
  }
]

You will get a 200 response with the response body being of type AttributeWriteResult[] where AttributeWriteResult is:

{
  "ref": {
     "id": "ASSET_ID",
     "name": "ATTRIBUTE_NAME"
   },
   "failure": "FAILURE REASON"
}

Where failure key will only be present if a problem was encountered writing that attribute's value with the value corresponding to one of the AttributeWriteFailure enum values.

This allows you to determine whether each write was successful or not.

1 Like

I see that you are PUTing a matrix with 2 asset id’s but they are the same:

  • can you use different assets id’s? I.e. is it possible to update many assets in a single call, which would be nice?
  • would sending JSON payload and create attributeLinks solve this too?

Yes you can use a mix of assetIds (behind the scene each write is treated separately so you get the result of each write in the response), some could succeed some could fail, etc.

You could also add attribute link configuration items to an attribute and then write to that attribute which would then automatically write to the linked attributes.

1 Like

Thanks @Rich , will go and experiment.

Hi @Rich, @michal ,

You are working with amateurs. I can’t seem to get it to work, so clearly I’m not doing something right.

Firstly, I deleted all the existing dockers I had, the used the same docker-compose.yml file from the wiki. I thought it should be ok, as it pulls the latest version of manager.

Then I did as I do normally running the following:

KEYCLOAK_FRONTEND_URL=https://192.168.0.139/auth docker-compose -p openremote up -d

Then I recreated the asset, and the attributes, giving them both public write access and store data.

I ran the following, and get the following response.

curl -i -k -X PUT -H “Content-Type:application/json” -d ‘[{“ref”:{“id”:“5yFt7lFcNKur4QXIezSoPw”,“name”:“PH”},“value”:5.42},{“ref”:{“id”:“5yFt7lFcNKur4QXIezSoPw”,“name”:“ORP”},“value”:10}]’ https://192.168.0.139/api/master/asset/attributes

HTTP/1.1 403 Forbidden

content-type: text/plain;charset=UTF-8

content-length: 83

date: Wed, 21 Apr 2021 09:48:57 GMT

Request failed with HTTP error status: 403 Forbidden

Please contact the help desk.

What have I done wrong in the process?

Looks to me like you don’t have the latest manager image as otherwise you wouldn’t get a 403 response…look at docker image ls and compare manager image ID with what is in docker hub,

docker-compose pull manager

Should definitely update your local manager image.

@Rich thanks for the response. Looks like I think that has sorted the problem! The image has definitely updated.

I’m now getting a 500 internal server error, although the parameters are being updated:

HTTP/1.1 500 Internal Server Error

content-type: text/plain;charset=UTF-8

content-length: 95

date: Wed, 21 Apr 2021 12:20:54 GMT

Request failed with HTTP error status: 500 Internal Server Error

Please contact the help desk.%

curl request: curl -i -k -X PUT -H “Content-Type:application/json” -d ‘[{“ref”:{“id”:“5yFt7lFcNKur4QXIezSoPw”,“name”:“PH”},“value”:8},{“ref”:{“id”:“5yFt7lFcNKur4QXIezSoPw”,“name”:“ORP”},“value”:15}]’ https://192.168.0.139/api/master/asset/attributes

In fact I’ve logged into the container and found this in the logs:

2021-04-21 14:15:19.955 INFO [main ] org.openremote.container.Container : >>> Runtime container startup complete
2021-04-21 14:18:25.076 INFO [WebService task-1 ] org.keycloak.adapters.KeycloakDeployment : Loaded URLs from http://keycloak:8080/auth/realms/master/.well-known/openid-configuration
2021-04-21 14:18:25.133 INFO [WebService task-2 ] org.keycloak.adapters.KeycloakDeployment : Loaded URLs from http://keycloak:8080/auth/realms/master/.well-known/openid-configuration
2021-04-21 14:18:57.052 INFO [WebService task-1 ] enremote.manager.asset.AssetResourceImpl : Write attribute value request: AttributeEvent{timestamp=Wed Apr 21 14:18:57 CEST 2021, attribut
eState=AttributeState{ref=AttributeRef{id=‘5yFt7lFcNKur4QXIezSoPw’, name=‘PH’}, value=5.42, deleted=false}}
2021-04-21 14:18:57.157 INFO [WebService task-1 ] enremote.manager.asset.AssetResourceImpl : Write attribute value request: AttributeEvent{timestamp=Wed Apr 21 14:18:57 CEST 2021, attribut
eState=AttributeState{ref=AttributeRef{id=‘5yFt7lFcNKur4QXIezSoPw’, name=‘ORP’}, value=10, deleted=false}}
2021-04-21 14:18:57.223 FINE [WebService task-1 ] emote.container.web.WebServiceExceptions : Web service exception in ‘RESTEasy Dispatch’ for ‘PUT http://192.168.0.139/asset/attributes
org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response object of type: [Lorg.openremote.model.attribute.AttributeWriteResult; of media type: application/oct
et-stream
at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$2(ServerResponseWriter.java:118)
at org.jboss.resteasy.core.interception.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:399)
at org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:219)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:95)
at org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:69)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:530)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:461)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:229)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:135)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:358)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:138)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:215)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:245)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:61)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.keycloak.adapters.undertow.UndertowAuthenticatedActionsHandler.handleRequest(UndertowAuthenticatedActionsHandler.java:66)
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.keycloak.adapters.undertow.ServletPreAuthActionsHandler.handleRequest(ServletPreAuthActionsHandler.java:69)
at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:387)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:841)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2019)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1558)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1449)
at java.lang.Thread.run(Thread.java:748)
2021-04-21 14:19:44.120 INFO [WebService task-1 ] enremote.manager.asset.AssetResourceImpl : Write attribute value request: AttributeEvent{timestamp=Wed Apr 21 14:19:44 CEST 2021, attribut
eState=AttributeState{ref=AttributeRef{id=‘5yFt7lFcNKur4QXIezSoPw’, name=‘PH’}, value=5.42, deleted=false}}
2021-04-21 14:19:44.153 INFO [WebService task-1 ] enremote.manager.asset.AssetResourceImpl : Write attribute value request: AttributeEvent{timestamp=Wed Apr 21 14:19:44 CEST 2021, attribut
eState=AttributeState{ref=AttributeRef{id=‘5yFt7lFcNKur4QXIezSoPw’, name=‘ORP’}, value=10, deleted=false}}

Try adding an Accept Header with a value of application/json, here’s an example curl:

curl -i -X PUT \
   -H "Content-Type:application/json" \
   -H "Accept:application/json" \
   -d \
'[
  {
    "ref": {
      "id": "6CJHsVExtMJ1DRmLDsywjb",
      "name": "notes"
    },
    "value": "test"
  },
  {
    "ref": {
      "id": "6CJHsVExtMJ1DRmLDsywjb",
      "name": "consoleName"
    },
    "value": "dadsda"
  }
]' \
 'http://localhost:8080/api/master/asset/attributes'

@Rich perfect! All working. Thanks for your assistance :slight_smile: