3forge REST Server¶
3forge provides its own REST server API for retrieving statistics about the 3forge session.
Note
If you are looking for documentation on the REST datasource adapter, see here.
Overview¶
The 3forge REST server is used for providing information about the 3forge session and the VM it's running in.
Generally, the REST endpoints are in two categories:
- Retrieving information about the 3forge session
- This includes information on JVM memory usage, user statistics, and more.
- Querying the Center
- This involves sending commands into the Center and requires authentication.
The default endpoint of the REST server is attributed to the web server's own ports followed by the REST query. For example: "localhost:33332/3forge_rest/stats".
To configure your 3forge session's REST Server API, see the following full list of properties. Otherwise, for a minimal configuration see the setup below.
Setup¶
The default configuration for the 3forge REST Server API as shipped in default.properties looks similar to this:
The plugin classes that ship natively with 3forge are the default endpoints, which are listed below.
To use a different address to the web port, you will need to refer to this configuration guide to set the appropriate properties.
Properties¶
A full list of configurable properties for REST can be found here, but the minimum required properties are the following:
Default Endpoints¶
The default endpoints supplied by 3forge are primarily aimed at developers to debug and retrieve information about the JVM state of the 3forge session.
While some endpoints are publicly available, others require admin or dev access. See the below table for a list of the default endpoints and their access requirements.
| Endpoint | Description | Authentication Required | Admin Only | Params |
|---|---|---|---|---|
/3forge_rest/version |
Returns detailed version information about the 3forge session | display=json or display=text |
||
/3forge_rest/whoami |
Returns the username and properties | display=json or display=text |
||
/3forge_rest/whatsmyip |
Returns the IP address of the client accessing the endpoint | display=json or display=text |
||
/3forge_rest/stats |
Public endpoint that returns statistics about the VM | display=json or display=text |
||
/3forge_rest/query? |
Executes a command against the Center and returns the result sets. See here for more information on query commands and parameters | full list here |
Note
All default endpoints are GET requests.
Authentication¶
Some of the default endpoints require authentication, admin permissions, or both.
Depending on how you access the REST Server, the method to do so will differ.
Basic Authentication¶
For the simplest authentication setup that uses a combination of a 3forge user's username and password, see the instructions below.
When accessing an endpoint that requires authorization via browser links, i.e: navigating to http://localhost:33332/3forge_rest/version, you will be prompted to login.
Enter your 3forge username and password details in the pop-up.
To send a curl request via a terminal, supply the username and password token using the Basic Authentication flag:
This will output the full JSON of the query endpoint, in this case version, in the terminal.
To send requests via the command-line or PowerShell, you will need to authorize your request with an Authorization header.
WebRequests must be encoded. Input the following into PowerShell, replacing username:password with your details:
This will output the full JSON of the query endpoint, in this case version, in the terminal.
Examples¶
These are examples of queries that can be passed to the REST API.
Examples assume the following that the 3forge session is being hosted on your local machine with default ports (localhost:33332). Please change these details according to your setup.
GET /3forge_rest/version¶
3forge session version information
Query Commands
| Params | Description | Options |
|---|---|---|
display |
What format to display the response | json or text |
Default:
With display params:
Default:
With display params:
Query Outputs
These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.
Display¶
Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/version), or with display=text (http://localhost:33332/3forge_rest/version?display=text)
This will return a webpage with information similar to the following:
Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/version?display=json).
This will return a webpage similar to the following:
Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:
GET /3forge_rest/whoami¶
Username and properties details of user accessing the 3forge session.
Query Commands
| Params | Description | Options |
|---|---|---|
display |
What format to display the response | json or text |
Default:
With display params:
Default:
With display params:
Query Outputs
These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.
Display¶
Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/whoami), or with display=text (http://localhost:33332/3forge_rest/whoami?display=text)
This will return a webpage with information similar to the following:
Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/whoami?display=json).
This will return a webpage similar to the following:
Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:
GET /3forge_rest/whatsmyip¶
IP address information of client accessing the endpoint.
Query Commands
| Params | Description | Options |
|---|---|---|
display |
What format to display the response | json or text |
Default:
With display params:
Default:
With display params:
Query Outputs
These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.
Display¶
Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/whatsmyip), or with display=text (http://localhost:33332/3forge_rest/whatsmyip?display=text)
This will return a webpage with information similar to the following:
Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/whatsmyip?display=json).
This will return a webpage similar to the following:
Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:
GET /3forge_rest/stats¶
3forge JVM statistics
Query Commands
| Params | Description | Options |
|---|---|---|
display |
What format to display the response | json or text |
Default:
With display params:
Default:
With display params:
Query Outputs
These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.
Display¶
Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/stats), or with display=text (http://localhost:33332/3forge_rest/stats?display=text)
This will return a webpage with information similar to the following:
Output from hitting the endpoint with the additional display=json parameter (http://localhost:33332/3forge_rest/stats?display=json).
This will return a webpage similar to the following:
Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab. Below is a sample of the Pretty Print formatted JSON output from this endpoint:
GET /3forge_rest/query¶
Use this endpoint to send Center commands via the REST API.
Query Commands
| Param | Description | Example |
|---|---|---|
cmd (required) |
localhost:33332/3forge_rest/query?cmd=SHOW+TABLES |
|
display |
text, json, jsonRows, jsonMaps, pipe |
localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=jsonRows |
timeout |
localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&timeout=1000 |
|
limit |
localhost:33332/3forge_rest/query?cmd=SELECT+*+FROM+__TABLE&limit=2 |
|
ds |
localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&ds=AMI |
|
show_plan |
on or off |
localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&show_plan=on |
string_template |
${...}, or to interpret them as literalson or off |
localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&string_template=off |
Basic SHOW TABLES Center command:
With display params:
Default:
With display params:
Query Outputs
These are example outputs as if called from within the browser. Calling via terminal will retrieve slightly different formatting.
Display¶
Output from hitting the endpoint with no additional parameters (http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES), or with display=text (http://localhost:33332/3forge_rest/query?cmd=SHOW+TABLES&display=text)
This will return a webpage with information similar to the following:
display=pipe¶
Returns a pipe-delineated text output of the table information:
Output from hitting the endpoint with the additional display=json parameters, of which there are several. Please refer to the table above for the different options.
This will return a webpage similar to the following:
Navigate between the different headers. To view/download the raw JSON, select the "Raw Data" tab.
Listed below are samples of Pretty Print formatted JSON from the different display options:
display=json¶
display=jsonRows¶
display=jsonMaps¶
Custom Endpoints¶
You can create your own custom endpoints using Java by implementing an instance of the AmiRestPlugin or AmiRestCenterPlugin interface. This is similar to how any custom plugin is implemented in 3forge.
Setup¶
To create a custom endpoint requires the following:
-
Create a Java package for your new endpoint. Include
autocode.jarandout.jarin the build classpath. -
Create a Java file that implements either the
com.f1.ami.amicommon.rest.AmiRestPluginorcom.f1.ami.center.AmiRestCenterPlugininterface depending on the type of REST endpoint you require. -
Save the package as a
.jarfile to then be added to your 3forge installation'samione/libdirectory. -
Add the new REST endpoint to your
local.properties:Note
$${ami.rest.plugin.classes}references the existing REST API endpoints. Do not remove it, and ensure to add any additional endpoints are added after in a comma-delimited list with no spaces.
Common Endpoint: Basic Example¶
This example demonstrates how to use the AmiRestPlugin interface to access a basic endpoint that displays "Hello World" for an admin user.
To do so, follow the steps below.
-
In an IDE of your choosing, create a Java package called
test_endpointand add the following to yourlocal.properties: -
Create and define the endpoint
HelloWorldby implementing theAmiRestPlugininterface in your Java IDE:AmiRestPlugin_HelloWorld.java¶ -
Save the project and export it as a
.jarfile into theamione/libdirectory of the 3forge installation. -
Restart 3forge and go to the REST API page. By default, this will be hosted on the same port as the web:
You should now see your custom endpoint in the available list:
-
Click on the endpoint, or paste the command directly into your browser. If you are logged in as an admin user, you will see the following:
Otherwise:
Center Endpoint: Example¶
REST endpoints can be used to query information in the Center, such as specific tables. The existing endpoint /3forge_rest/query is an example of a Center endpoint.
Implementing a custom Center endpoint requires the AmiRestCenterPlugin interface which allows a developer to query the Center within some custom Java code and display the output in a REST endpoint.
This example demonstrates how to use the AmiRestCenterPlugin interface to create the endpoint order. This endpoint can query and pull row(s) from a Center table named "orders" with the following user-supplied query parameters:
-
id- User supplied id parameter corresponding to some row(s) in the Center table "orders"
-
multRows- Whether the supplied ID can be found in multiple rows or not. Default is
true.
- Whether the supplied ID can be found in multiple rows or not. Default is
To implement, follow the steps below.
-
You will need a Center table called "orders" populated with some data. For the purposes of this example, we are using the following schema:
-
In an IDE of your choosing, create a Java package called
center_endpointand add the following to yourlocal.properties: -
Create and define the endpoint
Orderby implementing theAmiRestCenterPlugininterface in your Java IDE:AmiRestPlugin_Order.java¶1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
package center_endpoint; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import com.f1.ami.amicommon.AmiUtils; import com.f1.ami.amicommon.msg.AmiCenterQueryDsRequest; import com.f1.ami.amicommon.msg.AmiCenterQueryDsResponse; import com.f1.ami.amicommon.msg.AmiCenterRequest; import com.f1.ami.amicommon.msg.AmiCenterResponse; import com.f1.ami.amicommon.rest.AmiRestRequest; import com.f1.ami.center.AmiCenterUtils; import com.f1.ami.center.AmiRestCenterPlugin; import com.f1.ami.web.auth.AmiAuthUser; import com.f1.base.Row; import com.f1.base.Table; import com.f1.container.ContainerTools; import com.f1.container.RequestOutputPort; import com.f1.container.ResultActionFuture; import com.f1.container.ResultMessage; import com.f1.utils.CH; import com.f1.utils.PropertyController; import com.f1.utils.SH; import com.f1.utils.string.ExpressionParserException; import com.f1.utils.structs.Tuple2; public class AmiRestPlugin_Order implements AmiRestCenterPlugin { private ContainerTools tools; private RequestOutputPort<AmiCenterRequest, AmiCenterResponse> port; @Override public String getPluginId() { // Give your plugin a unique ID return "Order"; } @Override public void init(ContainerTools tools, PropertyController props) { this.tools = tools; } @Override public String getEndpoint() { // The URL endpoint to hit return "order"; } @SuppressWarnings("rawtypes") @Override public void handler(AmiRestRequest rr, AmiAuthUser user) { /* Implements the logic of the endpoint. We want to achieve a few things: - Send a specific query to the Center: Retrieve an order from "orders" with a user-supplied ID. - Our "orders" table can have multiple rows linked to an ID, allow for this to be passed. - Handle errors like an incorrect ID parameter. - Store the output values in a table and display them as a JSON. */ // Create a HashMap to store error messages HashMap<String, Object> err = new HashMap<>(); // Create a request object to hold the Center query. This is the Center request, NOT the REST request AmiCenterQueryDsRequest request = tools.nw(AmiCenterQueryDsRequest.class); // Check the user accessing the endpoint has permission to access the Center table byte permissions = AmiCenterUtils.getPermissions(this.tools, user); request.setPermissions(permissions); request.setInvokedBy(user.getUserName()); // Set the Center and table information for the query request request.setType(AmiCenterQueryDsRequest.TYPE_QUERY); request.setOriginType(AmiCenterQueryDsRequest.ORIGIN_REST); int timeout = 60000; int limit = 10000; String ds = "AMI"; String queryidParam = rr.getParam("id"); // user-supplied in the REST request if (SH.isnt(queryidParam)) { // No ID supplied error handling err.put("error", "id parameter required e.g. http://<your-ami-host>:<port>" + rr.getInnerRequest().getRequestUri() + "?id=3"); rr.printJson(err); return; } /* The user-supplied REST endpoint query parameters: - "id": id of a row (can be multiple) - "multRows": optional, whether the supplied id can correspond to multiple rows. If not supplied, default is "true" */ // AmiScript command for querying Center table with a specified id String cmd = "select * from orders where id == " + queryidParam; // Set whether or not the id can correspond to multiple rows String MultipleRows = rr.getParam("multRows"); // Center request handling request.setDatasourceName(ds); // Set datasource to AMI request.setQuery(cmd); // Center query request.setLimit(limit); request.setQuerySessionKeepAlive(true); request.setTimeoutMs(timeout); request.setIsTest(false); request.setAllowSqlInjection(false); request.setQuerySessionKeepAlive(false); ResultActionFuture<AmiCenterResponse> future = port.requestWithFuture(request, null); ResultMessage<AmiCenterResponse> result = future.getResult(timeout); if (result.getError() != null) throw new RuntimeException(result.getError()); AmiCenterQueryDsResponse action = (AmiCenterQueryDsResponse) result.getActionNoThrowable(); // values hash map stores the Center response including errors etc Map values = new HashMap(); if (!action.getOk()) { Map<String, Object> error = new HashMap<String, Object>(); Exception exception = action.getException(); if (exception == null) { error.put("message", "unknown error"); } else { error.put("message", exception.getMessage()); if (exception instanceof ExpressionParserException) { ExpressionParserException ee = (ExpressionParserException) exception; Tuple2<Integer, Integer> pos = SH.getLinePosition(ee.getExpression(), ee.getPosition()); error.put("atLine", pos.getA()); error.put("atChar", pos.getB()); } } values.put("error", error); } int numberOfRows = 0; // If the query does not return any values if (CH.isEmpty(action.getTables())) { err.put("error", "the table \'orders\' does not exist"); rr.printJson(err); return; } // Store the query response in values List<Table> tables = action.getTables(); Map l = new HashMap(); for (Table t : tables) { List<Map> rows = new ArrayList<Map>(t.getSize()); numberOfRows = t.getSize(); for (Row row : t.getRows()) rows.add(new HashMap(row)); l.put(t.getTitle(), rows); } values.put("tables", l); // Store the resulting table as a JSON Map<String, Object> tablesJson = (Map<String, Object>) values.get("tables"); Object orders = tablesJson.get("orders"); if ("{orders=[]}".equals(Objects.toString(values.get("tables")))) { err.put("error", "The 'id' value does not exist in the table."); rr.printJson(err); return; } // multRows handling List<Map<String, Object>> ordersList = (List<Map<String, Object>>) tablesJson.get("orders"); Map<String, Object> firstOrder = ordersList.get(0); if (!"false".equals(MultipleRows) && !"true".equals(MultipleRows) && MultipleRows != null) { err.put("error", "Invalid 'multRows' parameter. Acceptable values are 'multRows=true' or 'multRows=false' (default: 'multRows=true')."); rr.printJson(err); return; } if ("false".equals(MultipleRows)) { if (numberOfRows == 1) { rr.printJson(firstOrder); return; } err.put("error", "Multiple rows match the specified 'id'. Set 'multRows=true' if this is intentional."); rr.printJson(err); } if ("true".equals(MultipleRows) || MultipleRows == null) { rr.printJson(orders); return; } } @Override public boolean requiresAuth() { // Whether authorization is needed. Note that authorization is always needed for Center endpoints return true; } public void setItineraryPort(RequestOutputPort<AmiCenterRequest, AmiCenterResponse> itineraryPort) { // Allows the Center request to be sent. Do not change this this.port = itineraryPort; this.tools = port.getTools(); } }Note
Refer to the comments within the code snippet for details on the methods and implementation. If you require additional information, please reach out to us at support@3forge.com
-
Save the project and export it as a
.jarfile into theamione/libdirectory of the 3forge installation. -
Restart 3forge and insert some data into the table "orders." For example:
-
Go to the REST API page. By default, this will be hosted on the same port as the web:
You should now see your custom endpoint in the available list:
-
Click on the endpoint (you may be prompted to login). You will see the following error message:
This is the error message for not passing in a query. Supply a query with an id to the endpoint, e.g:
localhost:33332/3forge_rest/order?id=3 -
This endpoint also takes the optional
multRowsparam. Passing inlocalhost:33332/3forge_rest/order?id=12&multRows=truewill display the following:












