Difference between revisions of "Advanced"

From 3forge Documentation
Jump to navigation Jump to search
Tag: visualeditor
Tag: visualeditor
(12 intermediate revisions by 2 users not shown)
Line 21: Line 21:
  
 
== Creating a Method for being remotely called ==
 
== Creating a Method for being remotely called ==
Create a custom method with a predetermined "well-known" name that returns a Map, and takes an RpcRequest object as a single Argument of the form:
+
Create a custom method with a predetermined "well-known" name that returns a Map, and takes an RpcRequest object as a single Argument of the form:<syntaxhighlight lang="amiscript">
 
+
Map myMethod (RpcRequest request){
    Map myMethod (RpcRequest request){
+
    Map requestArgs = request.getArguments();
 
+
    //Do something here
          Map requestArgs = request.getArguments();
+
    Map responseArgs = new Map("key","someReturnValue");
 
+
    return responseArgs;
          //Do something here
+
}
 
+
</syntaxhighlight>Important: Please note that the return type of Map and argument type of RpcRequest is required and specifically indicates that the method can be called remotely.
          Map responseArgs = new Map("key","someReturnValue");
 
 
 
          return responseArgs;
 
 
 
    }
 
 
 
Important: Please note that the return type of Map and argument type of RpcRequest is required and specifically indicates that the method can be called remotely.
 
  
 
== Calling a Method remotely ==
 
== Calling a Method remotely ==
Line 46: Line 39:
 
''Arguments'' - Key/value pair of data to send. Note that the data will be transported using JSON, so complex objects, such as panels, cannot be referenced.
 
''Arguments'' - Key/value pair of data to send. Note that the data will be transported using JSON, so complex objects, such as panels, cannot be referenced.
  
For Example, let’s say the example above is running on a dashboard at ''some.host.com:33332''. This would call that method:
+
For Example, let’s say the example above is running on a dashboard at ''some.host.com:33332''. This would call that method:<syntaxhighlight lang="amiscript">
 
 
 
Map requestArgs=new Map("key","someRequestValue");
 
Map requestArgs=new Map("key","someRequestValue");
  
String host="<nowiki>http://some.host.com:33332/</nowiki>";
+
String host="http://some.host.com:33332/";
  
 
RpcRequest request=new RpcRequest(host,"myMethod",requestArgs);
 
RpcRequest request=new RpcRequest(host,"myMethod",requestArgs);
Line 57: Line 49:
  
 
Map responseArgs=response.getReturnValues();
 
Map responseArgs=response.getReturnValues();
[[File:RPCRequestPath.png|left|thumb]]
+
</syntaxhighlight>
  
== HOW AMI RCP Transport Works ==
+
== HOW AMI RPC Transport Works ==
  
 
# The Source dashboard calls session.callRpcSync(...)
 
# The Source dashboard calls session.callRpcSync(...)
Line 66: Line 58:
 
# The target webserver will receive the JSON, confirm headers and create an RpcRequest object for processing
 
# The target webserver will receive the JSON, confirm headers and create an RpcRequest object for processing
 
# The target dashboard's amiscript custom method will process the request and produce a response
 
# The target dashboard's amiscript custom method will process the request and produce a response
 
 
[[File:RPCRequestPath.png|315x315px]]
 
 
[[File:RPCRequestPath.png|none|frame]]
 
 
 
 
 
 
# The reply from the target server is sent back to the web browser in the http response as JSON
 
# The reply from the target server is sent back to the web browser in the http response as JSON
 
 
# The response is forwarded back to the source webserver via web sockets
 
# The response is forwarded back to the source webserver via web sockets
 
# AMI converts the JSON to an RpcResponse which is passed back to the dashboard for processing
 
# AMI converts the JSON to an RpcResponse which is passed back to the dashboard for processing
 +
 +
[[File:RPCRequestPath.png|frameless|378x378px]]
 +
 +
[[File:RPCResponsePath.png|frameless]]
  
 
 
 
 
  
== Error Condition Handling ==
+
==Error Condition Handling==
 
The '''session.callRpcSync(...)''' method will always return an '''RpcResponse''' object. You can inspect the Error for the success or failure status.  
 
The '''session.callRpcSync(...)''' method will always return an '''RpcResponse''' object. You can inspect the Error for the success or failure status.  
  
 
Here are the possible return values of '''getError''':
 
Here are the possible return values of '''getError''':
 
+
{| class="wikitable sortable"
               null - the rpc call executed successfully
+
!Return Value
 
+
!Description
    "NO_RESPONSE" - the server failed to respond, this is generally caused if the user refreshes the webpage while the rpc request is inflight, which will reset the connection.
+
|-
 
+
|null
               "CONNECTION_FAILED" - the CORS could not successfully establish a connection. This may be because the target url does not point to an existing AMI instance.
+
|The rpc call executed successfully
 
+
|-
               "INVALID_URL" - the format of the target url is not valid.
+
|"NO_RESPONSE"
 
+
|The server failed to respond, this is generally caused if the user refreshes the webpage while the rpc request is inflight, which will reset the connection.
               "ERROR_FROM_SERVER" - the server responded with an error.
+
|-
 
+
|"CONNECTION_FAILED"
           
+
|The CORS could not successfully establish a connection. This may be because the target url does not point to an existing AMI instance.
 
+
|-
               "ERROR_FROM_SERVER_ORIGIN_NOT_PERMITTED - The origin was not permitted (based on the ami.web.permitted.cors.origins option)
+
|"INVALID_URL"
 
+
|The format of the target url is not valid.
               "ERROR_FROM_SERVER_USER_NOT_LOGGED_IN" - the user does not have a browser open which is logged into the server
+
|-
 
+
|"ERROR_FROM_SERVER"  
               "ERROR_FROM_SERVER_NO_SUCH_METHOD" - the dashboard does not have a custom method of the supplied name. Or the method does not return a Map, or the method does not take an RpcRequest as it's single argument.
+
|The server responded with an error.
 
+
|-
               "ERROR_FROM_SERVER_METHOD_EXCEPTION" - The method was executed but threw an unhandled exception.
+
|"ERROR_FROM_SERVER_ORIGIN_NOT_PERMITTED"
 
+
|The origin was not permitted (based on the ami.web.permitted.cors.origins option)
               "ERROR_FROM_SERVER_USERNAME_MISMATCH" - The RpcRequest constructor was supplied a requiredUserName, which did not match the username currently logged in on the target dashboard.
+
|-
 
+
|"ERROR_FROM_SERVER_USER_NOT_LOGGED_IN"
               "ERROR_FROM_SERVER_LAYOUT_NAME_MISMATCH" - The RpcRequest constructor was supplied a requiredLayoutName, which did not match the name of the layout on the target dashboard.
+
|The user does not have a browser open which is logged into the server
 
+
|-
               "ERROR_FROM_SERVER_LAYOUT_NAME_MISMATCH" - The RpcRequest constructor was supplied a requiredSessionId, which did not match the sessionId of the layout on the target dashboard.
+
|"ERROR_FROM_SERVER_NO_SUCH_METHOD"
 
+
|The dashboard does not have a custom method of the supplied name. Or the method does not return a Map, or the method does not take an RpcRequest as it's single argument.
 +
|-
 +
|"ERROR_FROM_SERVER_METHOD_EXCEPTION"
 +
|The method was executed but threw an unhandled exception.
 +
|-
 +
|"ERROR_FROM_SERVER_USERNAME_MISMATCH"
 +
|The RpcRequest constructor was supplied a requiredUserName, which did not match the username currently logged in on the target dashboard.
 +
|-
 +
|"ERROR_FROM_SERVER_LAYOUT_NAME_MISMATCH"
 +
|The RpcRequest constructor was supplied a requiredLayoutName, which did not match the name of the layout on the target dashboard.
 +
|-
 +
|"ERROR_FROM_SERVER_LAYOUT_NAME_MISMATCH"
 +
|The RpcRequest constructor was supplied a requiredSessionId, which did not match the sessionId of the layout on the target dashboard.
 +
|}
 
Please Note, you can look at '''RpcResponse::getErrorMessage''' for further details.
 
Please Note, you can look at '''RpcResponse::getErrorMessage''' for further details.
  
== Security ==
+
== Security==
 
The ami.web.permitted.cors.origins option can be supplied (in the ami web's properties) to control which origins are accepted. See the Access-Control-Allow-Origin header for further information on how CORS origins works. Here are 3 separate examples:
 
The ami.web.permitted.cors.origins option can be supplied (in the ami web's properties) to control which origins are accepted. See the Access-Control-Allow-Origin header for further information on how CORS origins works. Here are 3 separate examples:
  

Revision as of 13:44, 15 March 2021

AMI Remote Procedure Calling (RPC)

AMI Remote Procedure Calling (RPC) Overview

AMI RPC leverages the HTTP CORS (Cross Origin Resource Sharing) standard to provide a mechanism for separate AMI dashboards within the same domain to communicate with each other.  As such, different teams working on distinct dashboards/use cases can easily integrate them.  For example, there are two teams, one building a dashboard for viewing orders and a second team building an order analytics dashboard. The first team could configure the order-dashboard such that when the user clicks on an order, an RPC instruction is sent to the other team’s dashboard, which in turn  displays analytics about the selected order. This paradigm promotes seamless integration across any number of dashboards at scale, providing users with a simple workflow accordingly.

How AMI RPC Works (User Perspective)

If a user is logged into two or more dashboards built on the AMI platform, then it's possible for the dashboards to call ami script across those dashboards.

Dashboards must be opened in the same browser space. For example, if one dashboard is opened in Chrome then the other dashboard must be opened in Chrome as well. Additionally, if one dashboard is opened in non-cognito mode then the other must also be opened in non-cognito mode.

Although it is not necessary for both dashboards to be logged in with same user ID, it can be enforced if necessary.

How AMI RPC Works (Configuration Perspective)

This is done by way of configuring custom AmiScript methods such that one dashboard can execute a custom AmiScript Method on another dashboard.  Communication supports full round trip, meaning the calling dashboard sends arguments to the receiving dashboard and then the receiving dashboard can respond to the call with data as well.

The target of such a call is identified using a host and method name combination.  It is common for the builder of the target dashboard to properly determine the host(s), method(s) and structure of the request and response payloads, which in aggregate is considered an API. This "API" is then communicated to those teams wishing to use the API.

For further reading on the general usage of CORS visit https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Please note that both dashboards must be hosted within the same domain, ex: analytics.acme.com and orders.acme.com are both in the acme.com so they can communicate via CORS, despite having different subdomains.  It's important that the all references to the domains use the fully qualified domain.

Creating a Method for being remotely called

Create a custom method with a predetermined "well-known" name that returns a Map, and takes an RpcRequest object as a single Argument of the form:

Map myMethod (RpcRequest request){
    Map requestArgs = request.getArguments();
    //Do something here
    Map responseArgs = new Map("key","someReturnValue");
    return responseArgs;
}

Important: Please note that the return type of Map and argument type of RpcRequest is required and specifically indicates that the method can be called remotely.

Calling a Method remotely

Use the callRpcSnyc(RpcRequest) method on the session object. The RpcRequest constructor takes these 3 arguments.

target - URL of the host that you wish to call the method on.

methodName - Name of the method to call (in the example above, it's myMethod).

Arguments - Key/value pair of data to send. Note that the data will be transported using JSON, so complex objects, such as panels, cannot be referenced.

For Example, let’s say the example above is running on a dashboard at some.host.com:33332. This would call that method:

Map requestArgs=new Map("key","someRequestValue");

String host="http://some.host.com:33332/";

RpcRequest request=new RpcRequest(host,"myMethod",requestArgs);

RpcResponse response = session.callRpcSync(request);

Map responseArgs=response.getReturnValues();

HOW AMI RPC Transport Works

  1. The Source dashboard calls session.callRpcSync(...)
  2. The Source dashboard creates a JSON request that is sent to the dashboard via websockets
  3. AMI's javascript forwards the JSON to the target Server (the browser will do CORS authentication against the target web server, which AMI is compatible with)
  4. The target webserver will receive the JSON, confirm headers and create an RpcRequest object for processing
  5. The target dashboard's amiscript custom method will process the request and produce a response
  6. The reply from the target server is sent back to the web browser in the http response as JSON
  7. The response is forwarded back to the source webserver via web sockets
  8. AMI converts the JSON to an RpcResponse which is passed back to the dashboard for processing

RPCRequestPath.png

RPCResponsePath.png

 

Error Condition Handling

The session.callRpcSync(...) method will always return an RpcResponse object. You can inspect the Error for the success or failure status.

Here are the possible return values of getError:

Return Value Description
null The rpc call executed successfully
"NO_RESPONSE" The server failed to respond, this is generally caused if the user refreshes the webpage while the rpc request is inflight, which will reset the connection.
"CONNECTION_FAILED" The CORS could not successfully establish a connection. This may be because the target url does not point to an existing AMI instance.
"INVALID_URL" The format of the target url is not valid.
"ERROR_FROM_SERVER" The server responded with an error.
"ERROR_FROM_SERVER_ORIGIN_NOT_PERMITTED" The origin was not permitted (based on the ami.web.permitted.cors.origins option)
"ERROR_FROM_SERVER_USER_NOT_LOGGED_IN" The user does not have a browser open which is logged into the server
"ERROR_FROM_SERVER_NO_SUCH_METHOD" The dashboard does not have a custom method of the supplied name. Or the method does not return a Map, or the method does not take an RpcRequest as it's single argument.
"ERROR_FROM_SERVER_METHOD_EXCEPTION" The method was executed but threw an unhandled exception.
"ERROR_FROM_SERVER_USERNAME_MISMATCH" The RpcRequest constructor was supplied a requiredUserName, which did not match the username currently logged in on the target dashboard.
"ERROR_FROM_SERVER_LAYOUT_NAME_MISMATCH" The RpcRequest constructor was supplied a requiredLayoutName, which did not match the name of the layout on the target dashboard.
"ERROR_FROM_SERVER_LAYOUT_NAME_MISMATCH" The RpcRequest constructor was supplied a requiredSessionId, which did not match the sessionId of the layout on the target dashboard.

Please Note, you can look at RpcResponse::getErrorMessage for further details.

Security

The ami.web.permitted.cors.origins option can be supplied (in the ami web's properties) to control which origins are accepted. See the Access-Control-Allow-Origin header for further information on how CORS origins works. Here are 3 separate examples:

# Disable CORS, no origins will be allowed to remotely call functions

ami.web.permitted.cors.origins=

# All origins are allowed

ami.web.permitted.cors.origins=*

# Allow a specific host

ami.web.permitted.cors.origins=http://myhost.com|http://thathost.com