Fully documented JavaScript component example
// -------------------------------------------------------------------
// Components
//
// In order to define a new component, you merely have to define global
// variables with specific names, which will act as annotations to tell
// RESTx about your component.
//
// BaseComponent has a parameterless constructor. If you need to define
// your own constructor, please keep in mind that it has to be a default
// constructor (no parameters).
//
// -------------------------------------------------
// Tell RESTx some information about this component.
// The following are well-known names that RESTx
// is looking for when dealing with a component.
// You must provide values for these three variables.
// Note that the 'name' does not have to be the same
// as the script name.
// -------------------------------------------------
this.name = "SampleComponent"
this.description = "One line description of the component"
this.documentation = "Longer description text, possibly multi-line, goes here"
// ---------------------------------
// Resource creation time parameters
// ---------------------------------
//
// Those parameters are required in order to create a resource for
// this component. As component author, you provide an object in
// this.parameters, which contains the names and definitions
// of each parameter.
//
// When a resource is accessed and an instance of your component is
// invoked then these fields are initialized by RESTx to the
// values that were provided for them during resource creation time.
// These fields become accessible as globally bound variables, hence
// directly accessible from anywhere within your script.
// That way you get automatic and easy access to resource creation
// time parameters from within your service methods.
//
// Each parameter is defined through an object, which encapsulates
// the definition of the parameter. Currently, only the following types
// are supported for parameters: STRING, PASSWORD, BOOLEAN, NUMBER
this.parameters = {
// With 'required' flag set, this parameter is mandatory.
// No default value needs to be specified.
someParameter : { type: TYPE.STRING, description: "Short description of this parameter", required: true },
// With 'required' flag not set, the parameter is optional.
// A default value must then be provided.
anotherParameter : { type: TYPE.NUMBER, description: "Short description of this parameter", defaultValue: 20 }
}
// ---------------------------------------------------------------
// Service method definitions.
//
// Each service method represents a sub-resource of whichever resource
// was created for this component.
//
// For example, if the resource URI is /resource/foo then the name of
// each service method appears as a possible sub-resource. If you have
// service methods 'bar1' and 'bar2' then the URLs of those sub-resources
// are /resource/foo/bar1 and /resource/foo/bar2.
//
// Service methods (and sub-resources) are needed in order to do something
// with a resource, since access to the base URL (/resource/foo) only
// gives you information about the resource.
//
// A full service method definition consists of a function with attributes
// defined on it. These attributes act as meta-information for RESTx. The
// description attribute is the only compulsory one. It contains a short
// human readable description of the service method.
//
// The service definition can contain attributes for defining the supported
// input and output MIME types. In their absence, RESTx uses all its supported
// types as a default. Based on the specified output types, RESTx performs
// content negotiation with the client. It automatically attempts to format
// the result of a service method according to the agreed upon format.
// Likewise, input (the message body) sent by the client is interpreted
// according to the specified input type and converted into an actual object -
// such as a map or dictionary - before it is being passed to the service method.
//
someService.description = "This is the XYZ subresource service"
someService.inputTypes = [] // Optional: Specify supported input content types. Use inputType for a single value.
// Specify an empty list here to indicate that no input is allowed.
someService.outputTypes = ["text/plain", "text/html"] // Optional: Specify supported output content types. Use outputType for a single value.
// The service definition may also contain a "parameters" object, if the service
// accepts any parameters. Parameters are then defined as fields of the
// "parameters" objects, using the same supported types as the ones used for
// resource-creation time parameters. These parameters can then be passed in the
// URI query string: /resource/foo/bar1?p1=foo
//
// Parameters can be defined to be positional. This allows the component author to
// list those service method parameters, which are allowed to appear in the URI
// path component.
//
// For example, if your service method 'bar1' declares three additional
// parameters (p1, p2 and p3) and p1 and p3 are defined as being positional
// then you can have a URI like this:
// /resource/foo/bar1/p1value/p3value?p2=p2value
// But even positional parameters may still be set ordinarily in the
// query portion of the URI, just like 'p2' is in our example.
//
someService.parameters = {
// Optional: add 'positional: true' to a parameter to specify it will be provided as part of the URL and not as a query string parameter
aNum : { type: TYPE.NUMBER, description: "This is a numeric parameter", required: false, defaultValue: 20 },
aBool : { type: TYPE.BOOLEAN, description: "This is a boolean parameter", required: false, defaultValue: true }
}
// ------------------------------------------------------------------
// Here is the actual service method body.
//
// Service methods always take at least two parameters: The HTTP request
// method and an input. In case of POST or PUT, the input is merely the body
// of the HTTP request interpreted according to the specified input types.
// It is null when another HTTP method is used.
//
// Any additional parameters that a service method expects need to be
// defined in the parameters object attribute of the function.
//
// Service methods always return a Result object, which allows you to
// set a specific HTTP status code, attach custom response headers and
// arbitrary items of return data. More examples of that below.
//
function someService(method, // The HTTP request method (a string). Always passed to service methods.
input, // Any data that came in the body of the request. Always passed to service methods.
aNum, // A numerical parameter. Specific to this particular service method.
aBool) // A boolean parameter. Specific to this particular service method.
{
// -------------------------------------------------
// Any kind of processing may take place here.
// No restriction on the available language features
// and accessible JDK libraries.
// -----------------------------------------------
// A few global variables are automatically bound to make
// life easier for the component author.
//
// HTTP access to external resources:
// ---------------------------------
// Need to access an external web-site? You can use any kind of client library
// you like, but for your convenience, BaseComponent provides some helper methods.
//
// Use the RESTx.httpGet() method, like so:
//
// result = RESTx.httpGet("http://example.com");
// or
// result = RESTx.httpGet("http://example.com", headers);
//
// where 'headers' is a hash map of additional HTTP request headers.
//
// result contains two public members: status and data, where data will most
// commonly be a string.
//
// Use RESTx.httpPost() to send data, like so:
//
// result = RESTx.httpPost("http://example.com", "data to send");
//
// Just like with RESTx.httpGet(), you may specify additional headers.
//
// If your request require HTTP basic authentication, you can just use the
// RESTx.httpSetCredentials() method once:
//
// RESTx.httpSetCredentials("username", "password")
//
// Subsequent RESTx.httpGet() and RESTx.httpPost() requests will use those credentials.
// File storage
// ------------
//
// No restrictions are placed on the component code. Therefore, you can access
// files and other storage any way you see fit.
//
// However, you often need means to store some simple data, which should persist
// between accesses of the same resource. You should be able to create, list,
// access and delete those stored items. An example of this is a queue or 'bag'
// resource, which provides service methods to store, list and access items
//
// This is a common pattern, so RESTx provides some simple facilities to help
// with that.
//
// Use RESTx.getFileStorage() to retrieve handle on your very own storage space.
// This is per-resource. So, other components (for other resources), or even the
// same component (if used behind a different resource) cannot see any of the items
// you have stored for this resource.
//
// fs = RESTx.getFileStorage();
// or
// fs = RESTx.getFileStorage("namespace");
//
// The latter form allows you to sub-divide your resource's name space further.
// You could maintain different name spaces for different items or service
// methods, for example.
//
// Once you have a handle on the file storage, you can use these very simple
// methods:
//
// buf = fs.loadFile("name");
// fs.storeFile("name", "content of file as string");
// fs.deleteFile("name");
// names = fs.listFiles();
//
// Accessing other resources
// -------------------------
//
// It is possible to easily access other resources that live on the same server.
// For this, use the accessResource() method:
//
// res = RESTx.accessResource("/resource/blah/foo");
// or
// res = RESTx.accessResource("/resource/blah/foo", "some data");
//
// In the latter case, the second parameter is the data that should be POSTed.
//
// If you want to use a different HTTP method, you can do:
//
// res = RESTx.accessResource("/resource/blah/foo", "some data", HttpMethod.PUT);
//
// Parameters can be passed to the resource either in the URI:
//
// res = RESTx.accessResource("/resource/blah/foo?p1=v1");
// or as a hash map:
// res = RESTx.accessResource("/resource/blah/foo", null, params);
//
// The 'data' element of the HttpResult object can contain the same type of
// data you prepare for a service methods's return value. This is described
// in detail below...
// Processing JSON:
// ----------------
// RESTx.fromJsonStr()
// RESTx.toJsonStr()
// -------------------------------------------------------------------------
// HTTP method constants:
// HTTP.GET
// HTTP.POST
// ...
// -------------------------
// Preparing the return data
// -------------------------
//
// A service method has to return a Result object. A Result object carries
// information about the desired HTTP status code, custom headers as well
// as any return data we might want.
//
// You can create a result object directly, like so:
//
// res = new org.mulesoft.restx.component.api.Result(200, "No problem")
//
// Or you can use one of several convenient factory methods, which return
// a Result object pre-initialized with the correct status code and possibly
// additional response headers. For example:
//
// res = RESULT.ok("No problem") // same as above
// or
// res = RESULT.ok()
// res = RESULT.created("/resource/foo/blah")
// res = RESULT.methodNotAllowed(method)
// res = RESULT.notFound("couldn't find the item")
// res = RESULT.badRequest("your request was bad")
// res = RESULT.noContent()
// res = RESULT.temporaryRedirect("http://example.com")
// res = RESULT.internalServerError("some big issues")
//
// Once you have created a Result object, you can also add your own return
// headers to it:
//
// res.addHeader("header-name", "value");
//
// Use the getStatus() and setStatus() methods of the Result object to get
// or set the status manually.
//
// Use getEntity() or setEntity() to get or manually set the return data.
//
// Return data can be an object of the following types: String, Boolean,
// Number, Map or List. Maps or Lists may contain elements of any of these
// types, including further Maps or Lists. Thus, it is possible to assemble
// lists or complex, hiearchical data structures as return values.
//
// Native JavaScript data structures like arrays and objects can be returned
// too: they'll get converted to Lists and Maps respectively.
//
// In this example we handle the HTTP methods in different ways, with different
// outcomes.
switch(method) {
case HTTP.GET : return RESULT.ok("Received 'GET' request, someParameter=" + someParameter)
case HTTP.POST: return RESULT.internalServerError("'POST' not yet implemented")
default : return RESULT.methodNotAllowed(method)
}
}