PalmService

Services in webOS generate asynchronous requests to local (DB, device info) or remote (Web) services.

Enyo services aim to improve the handling of asynchronous requests (or sequences of such requests), especially as they grow and become more complex.

This document focuses on PalmService (LS2) requests, but the same principles apply to all service requests. Indeed, they apply to all asynchronous operations modeled as Services.

Making Requests

Making a single service request

An Enyo service call is typically created in two parts: a component declaration and a method invocation on that component. The component describes the service endpoint and, optionally, how the results will be handled. The method invocation kicks it all off. The purpose of the component declaration is the same as for all components: to bring important flow information to the top level and refactor boilerplate code.

  1. Add a Service component to describe as much as you know ahead of time about the request and its handling:

    {
      name: "listAccounts",
      kind: "PalmService",
      service: "palm://com.palm.service.accounts/",
      method: "listAccounts",
      onSuccess: "gotAccounts",
      onFailure: "genericFailure"
    }
    
    

    Declaring the component does not actually start a service request; it just sets everything up ahead of time, so you can make the request later with a minimum of configuration. Be sure to attach the component to the most appropriate owner--the owner's lifecycle should fit the desired lifetime of the service call.

  2. Call the service with some parameters:

    this.$.listAccounts.call({
      capability: "MAIL"
    });
    
    
  3. Handle the response in your onSuccess/onFailure/onResponse handler:

    gotAccounts: function(inSender, inResponse) {
      console.log("got these accounts: " + enyo.json.to(inResponse));
    }
    
    

    Response handlers are just specialized event handlers, so inSender here is the service. For PalmService objects, inResponse is a pre-parsed JavaScript object.

    The different response events are fired under the following conditions:

    • onSuccess is called on a "successful" response. For PalmService objects, this is called if returnValue === true.
    • onFailure is called on a failure response.
    • onResponse is called on any kind of response.

You can also defer assignment of all the other properties of the request until the call is actually made. This is handy when you want to re-use a single service for different kinds of requests:

{name: "accounts", kind: "PalmService", onFailure: "genericFailure"}
...
this.$.accounts.call(
  {
      capability: "MAIL"
  },
  {
      method: "listAccounts",
      onSuccess: "gotAccounts"
  }
);
this.$.accounts.call(
  {
      ...
  },
  {
      method: "createAccount",
      onSuccess: "createdAccount"
  }
);

If you are calling a subscription service, you must set the subscribe property to true. This adds a "subscribe": true value to the params of the service call and also tells the PalmService object to defer the automatic destruction behavior that normally happens after your response callback. You can call the destroy method of the PalmService component when you want to unsubscribe from the service.

Making simultaneous service requests

After declaring your components, just make multiple calls in a row:

this.$.setPhonePref1.call();
this.$.setPhonePref2.call();
this.$.setPhonePref3.call();

Making a series of service requests

This is where declaring components ahead of time can help:

{name: "resizePhoto", ... onSuccess: "handleResizePhoto"},
{name: "copyPhoto", ... onSuccess: "handleCopyPhoto"},
{name: "saveContact", ... onSuccess: "switchToDetailsView"}

The handler code looks like this:

saveClick: function()
  this.$.resizePhoto.call({...});
},
handleResizePhoto: function(inSender, inResponse) {
  this.$.copyPhoto.call({
      srcPath: inResponse.path
  });
},
handleCopyPhoto: function(inSender, inResponse) {
  this.contact.photo = inResponse.path;
  this.$.saveContact.call(this.contact);
}

Published Properties

The following table summarizes the properties published by PalmService.

Name Default Description
method "" Name of method to call
subscribe null Should be set to true if you're calling a method that returns multiple results.
resubscribe false Set to true to have your subscription automatically restarted when it gets a failure response.
params null Object containing parameters for the service call

Extending PalmService

Encapsulating a frequently-used service can save you from typing out the service property repeatedly:

{
  name: "listAccounts", kind: "PalmService",
  service: "palm://com.palm.service.accounts/",
  method: "listAccounts", onSuccess: "gotAccounts",
  onFailure: "genericFailure"
},
{
  name: "createAccount", kind: "PalmService",
  service: "palm://com.palm.service.accounts/",
  method: "createAccount", onSuccess: "createdAccount",
  onFailure: "genericFailure"
}

Instead, make a kind for the bus service...

enyo.kind({
  name: "AccountsService",
  kind: "PalmService",
  service: "palm://com.palm.service.accounts/"
});

...and declare instances of that kind.

{
  name: "listAccounts", kind: "AccountsService", method: "listAccounts",
  onSuccess: "gotAccounts", onFailure: "genericFailure"
},
{
  name: "createAccount", kind: "AccountsService", method: "createAccount",
  onSuccess: "createdAccount", onFailure: "genericFailure"
}

Also, since the method value defaults to the component name, you can leave it out if the requests have unique names:

{name: "listAccounts", kind: "AccountsService", onSuccess: "gotAccounts",
  onFailure: "genericFailure"},
{name: "createAccount", kind: "AccountsService", onSuccess: "createdAccount",
  onFailure: "genericFailure"}

Mock Palm Services

Any PalmService responses can be easily mocked out without changing any code. Add a mock/ folder to your app and for each PalmService, create a file whose name is of the form:

GRANDPARENT_PARENT_SERVICE.json

So, for a "listAccounts" PalmService in a "SampleApp" component, create a file named mock/SampleApp_listAccounts.json.

In each mock JSON file, the service response JSON should look just as if it came from a real service:

{
  "returnValue": true,
  "results": [{
      "_id": "1",
      "_kind": "com.palm.account:1",
      "username": "username@example.com"
  }, {
      "_id": "2",
      "_kind": "com.palm.account:1",
      "username": "support@palm.com"
  }]
}

When the app is run in a browser environment, PalmService will automatically fall back to using the mock data.