Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Org.eclipse.higgins.js.pds.client

Revision as of 20:38, 19 September 2011 by Ptrevithick.gmail.com (Talk | contribs) (Common Functions)

{{#eclipseproject:technology.higgins|eclipse_custom_style.css}}

Files

Design Notes for API additions to this component

Objective

Create a general purpose API that JavaScript programs executed by HBX can use to get and set attributes about the user. The APIs are to support various kind of JavaScript apps executed by HBX. These HBX JavaScript apps can fill forms, scrape pages, auto-login to sites, as well as lots of other things with data that they read from a context, transforming it, correcting it, and writing it back in.

Summary:

  • getAttributes() - reads a set of attributes from the local ADS data cache in the browser
  • getSuggestions() - reads back all possible values of a given attribute. Useful for supporting interactive typeahead UI.
  • setAttributes() - writes a set of attributes to the local ADS data cache in the browser

getAttributes(domain, audience, attributes[], where[], future)

Enables a JavaScript app to request attributes (e.g user's email address, gender, etc.) about the user. It searches the person graph to find the requested attributes. If it finds multiple, ambiguous values for one or more attributes, it returns nothing (and the "where' parameter must be used to refine the query).

  • The goal of this rather high-level API is to shield the developer from the complexities of the multi-contextual, multi-person persona data model.
  • Depending on how the calling JavaScript is written, information about the user may remain local (i.e. within the browser and/or on the user's machine) or it may be transmitted to some external web service. The identity of the calling application and the identity of the "domain" are parameters to this call.
  • FUTURE: By setting the value of the authorities = the JavaScript developer's domain (i.e. the "issuer" id of the context associated with this JavaScript) it will only read attributes from this context.
  • FUTURE: If the attribute release is not part of (i) an explicit pre-existing connection/relationship with the domain or (ii) an implicit relationship (e.g. form filling when the user is viewing the page) then a UI should be implemented informing the user as to the identity of the agent, the identity of domain (the "next hop" destination if any) and the set of requested (required/optional) attributes and allowing them to give consent to this attribute disclosure.

Parameters:

  • domain: a string identifier of ultimate attribute data consumer as far as this is known.
  • audience: FUTURE (ignored for now) - String. Must match either the agent or the domain parameter value or be nil. If not nil, then indicates whether to encrypt tokens for the agent or the domain.
  • attributes[]: a set of (attribute, optional, authorities) tuples. attribute is a URI indicating the attribute type; the attribute may have one or more role params attached (e.g. ?r=Buyer); optional is a boolean (if true then this attribute is desired but not required)--for now this must be false. authorities will be used in the future; it must be nil for now. it is a list of domains that are considered by the caller as authoritative WRT this attribute and thus must be used as the source of the attribute, if this list is nil then self asserted values are acceptable. The value of a member of authorities is matched against the issuer of the containing context of the entity.
  • where[]: a set of (attribute, value-expression) pairs. attribute is the attribute URI of the form <namespace>#localAtt and namespace is the URL of a page (or a set of pages with a consistent schema). If the entire site uses the same schema then the site domain will suffice. value-expression (for now) may be an exact value (e.g. "Alice") or may be a regex that the value must match. In the short term the so-called "regex" is just a string of one or more characters followed by an asterisk (e.g. 2*, or 25*)--meaning that the value of the attribute must start with these characters (e.g. values "290" and "250" will match "2*" and "250" (only) will match "25*")
  • tokenTypes: A list of token types that are understood by the entity consuming the attributes requested. At present this must be a JSON document
  • future: (optional) a Futures object to handle asynchronous completion of this function

Returns:

  • A set of zero or more tokens (JSON documents at present). Each token encodes multiple (attribute, value(s), ambiguous) tuples where:
    • Attribute is the attribute URI (note: this will be one of the attribute URIs passed in to this method)
    • Value(s) are the value(s) of the attribute (may be nil, if no values could be found or ambiguous). Each value may be literal or object typed.
    • Ambiguous is true if more than one possible value could have been returned. False otherwise.

Implementation:

We start with an empty list called rt[] of (attribute, value, ambiguous) tuples where attribute is the long (URI) form of the attribute; value is the value of the attribute or nil; ambiguous is a boolean that indicates that there were multiple alternative values found (and thus a nil value is returned). We are careful that we pull in a logically self-consistent manner. That is, we do not pull inter-related attributes from different entities (e.g. person nodes). For example we would never pull a vcard:street from a v:adr object and the vcard:postal-code from another v:adr object.

  • context := getConnectionContext(domain) // e.g. http://azigo.com/ptrevithick/nytimes.com
  • ra[] := findRoles (template, attributes[]) // rolesAtts[] is a set of (roles[], atts[]) pairs where atts is an (attribute, optional, authorities[]) triple, and roles[] is a set of roles
  • ra[] := sortByRole (ra[])

(1) We try to pull values starting at the person within context

  • person := context concatenated with "#me" // e.g. http://azigo.com/ptrevithick/nytimes.com#me
  • If context exists and person has at least one attribute then
    • template := getConnectionTemplate(domain)
    • person := context + "#me"
    • findAttributeValues(template, person, rolesAtts[], where[], rt[])
  • Endif

(2) We try to pull values starting at the root person in the root context

  • person := rootPerson // root node in root context
  • findAttributeValues(template, person, rolesAtts[], where[], rt[])

(3) Convert rt into JSON and return rt

findRoles (template, atts[])

Determine roles for each attribute in atts[] if any.

Parameters:

  • template: the template context that has the Person class that has the mapping rules for these attributes
  • atts[]: a set of (attribute, optional, authorities[]) triples

Implementation:

  • Create a new empty set ra. ra is a set of (roles[], atts[]) pairs where atts is an (attribute, optional, authorities[]) triple, and roles[] is a set of roles
  • Loop: For every triple t in atts
    • roles[] := getRoles(t.attribute) // extract role params from URI (if any)
    • class := getNamespace(t.attribute) // e.g. http://nytimes.com
    • att := getLocalAtt (t.attribute) // e.g. "bFirstName"
    • rule := getRule (template, class, att)
    • If rule is not nil then
      • Add the value of map:param attribute of the rule to roles[]
    • EndIf
    • Add a new pair (roles[], t) to ra[]
  • EndLoop
  • Return ra

sortByRole (ra[])

Combine together all pairs in ra[]that have the exact same roles[] component (thus increasing from exactly one to N>=1 the number of attribute-triples in each pair)

  • So e.g. we have these three pairs as input:
    • ((Buyer, eCommerce), (firstname, nil, nil))
    • ((Buyer, eCommerce), (lastname, nil, nil))
    • ((nil), (postalcode, nil, nil))
  • We would return two pairs:
    • ((Buyer, eCommerce), ((firstname, nil, nil),(lastname, nil, nil)))
    • ((nil), (postalcode, nil, nil))

Parameters:

  • ra (by reference): a set of (roles[], atts[]) pairs where atts is an (attribute, optional, authorities[]) triple, and roles[] is a set of roles

Implementation:

  • TODO

findAttributeValues (template, topPerson, rolesAtts[], where[], rt[])

Each of the N pairs of (roles[], atts[]) in rolesAtts, can be thought of as a request to (a) find person nodes whose roles match roles[] and (b) retrieve the values of the atts[] attributes from this/these persons making sure that if there are N>1 values for a given attribute that we return nil as the value for this attribute (and flag it as ambiguous).

Parameters:

  • template: the template context that has the Person class that has the mapping rules for these attributes
  • topPerson: the topmost person node to search down from
  • rolesAtts[]: a list of (roles[], atts[]) pairs where atts is an (attribute, optional, authorities[]) triple
  • where[]: a set of (attribute, value-expression) pairs.
  • rt[]: set of (attribute, value, ambiguous) triples

Initialize rt[]:

  • Loop: For every triple t in ra.atts[]
    • Add (t.attribute, nil, true) tuple into rt[]
  • EndLoop

Implementation:

  • Loop: For every pair ra in rolesAtts[]
    • sourcePersons[] := findPersonsByRole (topPerson, ra.roles[])
    • sourcePersons[] := cullByWhere (template, sourcePersons[], where[])
    • Loop: For every Person p in sourcePersons
      • extractValues (template, p, ra.atts[], rt[])
    • EndLoop
  • EndLoop

findPersonsByRole(topPerson, roles[])

Return a list of p:Person nodes found that hold the required role value(s). Start the search at topPerson and follow h:correlation links (and tunneling through Proxy objects).

Parameters:

  • topPerson: topmost person at which to begin search
  • roles[]: list of required roles to match

Implementation:

  • Initialize empty set rtn[]
  • Loop: For every person p reachable (by traversing h:correlation links) from topPerson (including topPerson)
    • If the set of values of the role attribute of p is the same set (or a super-set) as roles[] //we've found a role-matching person
      • add p to rtn[]
    • EndIf
  • EndLoop
  • Return rtn[]

cullByWhere (template, persons[], where[])

Given a set of persons, persons all of which could be sources for attributes, return a set of persons that satisfy all where parameters, where.

Parameters:

  • template: template containing mapping rules for any attribute mentioned in where
  • persons[]: a set of Persons
  • where[]: a set of (attribute, expression) pairs

Implementation:

  • Loop: For every person p in persons[]
    • if not hasAttributeAndMatchesExpression (p, where[]) then
      • remove p from the set persons[]
    • EndIf
  • EndLoop
hasAttributeAndMatchesExpression (person, attexp[])

Return true if person person has all attributes in attexp[] and for each attribute its value matches the corresponding expresssion in attexp[]

Parameters:

  • person: a p:Person
  • attexp[]: a set of (attribute, expression) pairs

Implementation

  • Loop: For every pair ae in attexp[]
    • att := getLocalAtt (ae.attribute) // e.g. "bFirstName"
    • class := getNamespace (ae.attribute) // e.g. http://nytimes.com
    • rule := getRule (template, class, att)
    • value := pullEvalRule (rule, person) // pull within the scope of person
    • If value is nil then return nil
    • If value does not match expression then return nil
  • EndLoop
  • Return true

extractValues(template, person, atts[], rt[])

Extract the values of all members of atts from person node person. Parameters:

  • template: template that has the Person class with the mapping rules for the namespace of attributes in atts[]
  • person: person node to extract attributes from
  • atts[]: a set of (attribute, optional, authorities) triples
  • rt[]: set of (attribute, value, ambiguous) triples

Implementation:

  • Loop: For every triple t in atts[]
    • att := getLocalAtt (t.attribute) // e.g. "bFirstName"
    • class := getNamespace (t.attribute) // e.g. http://nytimes.com
    • rule := getRule (template, class, att)
    • value := pullEvalRule (rule, person) // pull within the scope of person
    • If value is not nil then
      • If rt[] contains a tuple whose attribute matches t.attribute and that has a non-nil value then
        • Replace the value of value in that tuple with nil
        • Set the value of ambiguous in that tuple to true
      • Else
        • Add a new tuple (t.attribute, value, false) to rt[]
        • Remove triple t from atts[]
      • EndIf
    • EndIf
  • EndLoop

pullEvalRule (string rule, URI person)

We evaluate the rule in "pull" mode (we're pulling values from the PDS) and from person person. We dispatch functions by name.

  • mappingRule := getRuleName(rule) // e.g. "rolePathLiteral"
  • If mappingRule == "rolePathLiteral" then
    • value := pullRolePathLiteral(rule, person)
    • return value
  • If (mappingRule == ... ) then ...
pullRolePathLiteral(rule, person)

Pull value within scope of person

Parameters:

  • rule: a rule object. Has subfields:
    • rdf:type (e.g. map:rolePathLiteral) - the name of the mapping function (ignored here)
    • map:predicate (E.g. :bFirstName) - the attribute we are trying to get the value of (ignored here)
    • map:role (e.g. p:Buyer) - an optional field (ignored here because it has already played its part in helping to select person)
    • map:path (e.g. vcard:n) - object valued attribute of person
    • map:mappedAtt (e.g. vcard:given-name) - attribute of object to read
  • person: we begin the search at the person person

Implementation:

  • path := getPathParam(rule)
  • mappedAtt := getMappedAtt(rule)
  • obj := value of path attribute of person
  • If obj is nil then return nil
  • Return the value of mappedAtt attribute of obj

Example

getSuggestions (attribute, future)

This method is only needed for interactive form filling use cases.

Note: For privacy reasons this method may only be called by highly trusted JavaScript.

Parameters:

  • attribute: attribute URI of the form <namespace>#<localAtt>
  • future: (optional) a Futures object to handle asynchronous completion of this function

setAttributes (domain, attributes[])

Takes a set of attribute/value pairs. It replaces the existing values of the same attribute (if they exist) otherwise adding them. All other attribute's values are unaffected.

The attribute URIs passed in are in a namespace that is either the FlatPersona vocabulary or in some other vocabulary that is defined in an ontology file that contains mapping rules that describe how to transform between the vocabulary and the Persona data model. A attribute passed as a parameter may map to a traversal path through a graph of objects in the persona data model. For example, if one of the attributes passed in is fp:givenName then this attribute maps to a path that originates in a person node in a context whose identity is derived from domain, follows a vcard:n link (complex attribute) whose value is an instance of vcard:Name, and arrives at the vcard:given-name attribute of this vcard:Name object. If either the person object, the vcard:Name object or both did not exist prior to invoking this method then they would be constructed on-the-fly as a side-effect of this method.

Parameters:

  • domain: domain of the party providing the attribute value
  • attributes: an array of (attribute, value) pairs. The attribute is a URI of the form <namespace>#<localAtt>. For scraping <namespace> is the URL of the page being scraped (with query parameters removed). The value is a literal value or object (URI) value.

Implementation:

(1) Phase I

  • context := getConnectionContext(domain) // e.g. http://azigo.com/ptrevithick/nytimes.com
  • template := getConnectionTemplate(domain) // e.g. http://azigo.com/sys/template/nytimes.com
  • Loop. For every (attribute, value) pair p in attributes[]
    • class := getNamespace(p.attribute) // e.g. http://nytimes.com
    • att := getLocalAtt(p.attribute) // e.g. "bFirstName"
    • rule := getRule(template, class, att)
    • newPerson := pushEvalRule(context, rule, value)
    • if newPerson is not nil then add to newPeople list
  • EndLoop

(2) Phase II

  • Loop: For every new person np within newPeople
    • Loop: For every existing person ep reachable from the root me in the root context
      • If np is equivalent to ep then
        • Delete np
      • Else
        • Add an h:correlation link from root me to np

URI pushEvalRule (URI context, string rule, value)

We evaluate the rule in "push" mode (we're pushing values in to the PDS). Within the rule string there is the name of the rule to fire, and a set of parameter/values. We call dispatch functions by name.

  • mappingRule := getRuleName(rule) // e.g. "rolePathLiteral"
  • If mappingRule == "rolePathLiteral" then
    • newPerson := pushRolePathLiteral(context, rule, value)
    • return newPerson
  • ElseIf mappingRule == ...

URI pushRolePathLiteral (URI context, string rule, value)

Push value into PDS.

  • role := getRoleParam(rule)
  • path := getPathParam(rule)
  • mappedAtt := getMappedAtt(rule)
  • If role == nil then
    • If path exists from :me within context context
      • Then follow it
      • Else build portions of the path as needed // build objects along the path to create the path as needed
    • Replace the value of mappedAtt with new value
  • Else
    • Find a person from :me one hop out along h:correlation links that has role value of role
    • If it isn't there then
      • newContext := a new context
      • newPerson := a new :me person within newContext
      • Create an h:correlation link from the :me within context to the :me within newContext
    • Else
      • Walk path/mappedAtt and set the value
      • newPerson := nil
  • EndIf
  • Return newPerson

Common Functions

URI getConnectionContext (string domain)

Return:

  • http://<servername>/<pdsuser>/domain

URI getConnectionTemplate (string domain)

Return:

  • http://<servername>/sys/template/domain

string getLocalAtt (URI attribute)

Return everything to the right of the #. (e.g. if the input was http://nytimes.com#bFirstName then return "bFirstName").

getRoles (URI attribute)

Return the value(s) of "r" (role) parameter on URI. (e.g. if the input was http://www.eclipse.org/higgins/ontologies/2010/6/fp#givenName?r=Buyer then return ("Buyer")).

URI getNamespace (URI attribute)

Return all but the # and beyond. (e.g. if the input was http:///nytimes.com#bFirstName then return http://nytimes.com).

string getRule (URI template, URI class, string localAtt)

Find a matching mapping rule in the class entity class within this template. Look through all the values of spin:rule attribute (or sub-attribute, e.g. map:nameRule) of the entity class such that the attribute map:predicate matches localAtt (e.g. bFirstName). For example here is a match:

     map:nameRule
             [ rdf:type map:rolePathLiteral ;
               map:mappedAtt <http://www.w3.org/2006/vcard/ns#given-name> ;
               map:path <http://www.w3.org/2006/vcard/ns#n> ;
               map:predicate :bFirstName ;
               map:role p:Buyer
             ] ;

The above rule includes the optional role attribute.

URI getTemplate (URI context)

Returns value of template attribute of context.

Back to the top