Skip to content

JavaScript BrAPI Client with support for asynchronous interdependent calls.

License

Notifications You must be signed in to change notification settings

solgenomics/BrAPI-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BrAPI.js

BrAPI.js is a JavaScript client library for BrAPI. The call style of this library is inspired by D3.js. It can be used in either a browser or a Node.js application. It uses the Fetch API (or node-fetch in Node.js) for making AJAX calls. BrAPI.js is dependent on ES6 classes.

BrAPI.js supports BrAPI versions v1.0-v2.0. Currently, it expects a server to use a single version.

Contents

Installation

# Be sure your version of NPM supports 'prepare' scripts (>=npm@4.0.0)
# Recommended:
npm install git+https://github.com/solgenomics/BrAPI-js
# Otherwise:
git clone https://github.com/solgenomics/BrAPI-js.git
cd BrAPI.js
npm install . 

Include/Require

<!--Browser-->
<script src="build/BrAPI.js" charset="utf-8"></script>
// ES6 import
import BrAPI from './build/BrAPI.js';
// Node.js
const BrAPI = require('./BrAPI.js');

How it Works

BrAPI.js has been designed to allow for many simultaneous and interdependent calls to BrAPI to be performed asynchronously. In order to do this, data are managed by a class of objects called BrAPINodes. Nodes are organized into a DAG which represents dependancies. Each node represents a transformation on the data. Basic transformations include fork, join, map, reduce, and filter. BrAPI calls are special transformations. Each BrAPI call can be either a fork (calls returning array data) or a map (calls returning single objects). Data interacts through nodes via tasks. When a task completes, it triggers the creation of new task(s) in all child nodes. With the exception of reduce, the operations can be performed independently on each datum. This means one datum can be transformed across multiple nodes while another moves more slowly.

Usage Reference

Initialization and Configuration

# BrAPI(address, [version, auth_token, call_limit, credentials]) <>

Creates a root BrAPINode. This is the root of a BrAPI.js DAG dataflow. The address should be a string with the base URL of a BrAPI instance that is being queried, i.e. "https://www.yambase.org/brapi/v1". If an auth_token is provided, it will be added as a Bearer header when sending requests over HTTPS. Changing the version determines which deprecation/removal warnings are written the console, it does not restrict functionality. The call_limit parameter specifies how many simultaneous requests may be run by this node and its descendants against the specified server. The credentials parameter allows you to define how HTTP credentials (aka cookies) should be included in a request. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials for more information.

Examples:
var brapi_root = BrAPI("https://www.yambase.org/brapi/v1")
var brapi_root = BrAPI("https://www.yambase.org/brapi/v1", "v1.2", "myToken")
var brapi_root = BrAPI(
    "https://www.yambase.org/brapi/v1",
    null, //Don't specify version
    "myToken",
    18 //Allow 18 simultaneous requests
)

If you have multiple different versions of BrAPI for different calls on your server, you may have to create two separate BrAPI handlers. For example, if your search calls are v1.2 and everything else is v1.3, you would have to use one of the following options:

var brapi_root1 = BrAPI("https://www.myserver.org/brapi/v1","v1.2") // for your search calls
var brapi_root2 = BrAPI("https://www.myserver.org/brapi/v1","v1.3") // for your other calls.
var brapi_root1 = BrAPI("https://www.myserver.org/brapi/v1","v1.2") // for your search calls
    .germplasm_search(...)
    .server("https://www.myserver.org/brapi/v1","v1.3") // switch for your other calls.
    .germplasm_progeny(...)
    .each(...);

# node.server(address, [version, auth_token, call_limit, credentials]) <>

Creates and returns a child BrAPINode which changes the BrAPI server instance queried by all descendants.

Examples:
BrAPI("https://www.yambase.org/brapi/v1")
    // Query accessions containing "MyAccession" from yambase.org
    .germplasm_search({germplasmName:"MyAccession"})
    // ** Switch to yamMarkerBase.net **
    .server("https://www.yamMarkerBase.net/brapi/v1")
    // Using results from the first query, search the second server
    // (one search for each result from the first)
    .germplasm_search(function(germ){
        return {germplasmName:germ.germplasmName}
    });

# node.data(array) <>

Creates and returns a child BrAPINode which ignores any completed tasks from it's parent node and creates a new task in each child node for each datum in the array. This is the main way of adding input to the dataflow and is only available as a child of the root node, or on a server node that is the immediate child of a root node.

Examples:
BrAPI("https://www.yambase.org/brapi/v1")
    // Set ["344","567","223"] as data
    .data(["344","567","223"])
    // perform a germplasm call with each datum as germplasmDbId
    .germplasm(function(d){
        return {germplasmDbId:d};
    })
    .all(function(germplasm_objects){
        //prints a list of germplasm objects
        console.log(germplasm_objects)
    });

BrAPI Call Nodes

# node.{{BrAPI_Method}}(params [, behavior]) <>

A {{BrAPI_Method}} is be one of the available BrAPI methods. These methods create and return a child BrAPINode which makes the relevant BrAPI calls. Calls are tracked as tasks and occur asynchronously. metadata.asynchStatus asynchronous BrAPI calls are also supported and a polling time of 15 seconds is used. Parameters (either URL parameters or body parameters) are specified for the call using the params argument. The params argument can either be an object, or a function. If it is a function, a separate BrAPI call will be made for each datum passed by the parent node. Otherwise, a single call (or series of calls for each page) will be made and the input data will be ignored. For each datum created, the full response from which it was extracted is available via datum.__response.

There are two special parameters specific to BrAPI.js. The first, pageRange (an array [first_page, last_page]), controls pagination and must be used in place of the usual BrAPI page parameter. pageRange defaults to [0,Infinity]. The second, HTTPMethod, allows one to override the default HTTP method (e.g. "POST","GET") for a BrAPI method.

For calls which return a response containing a data array, (i.e. node.germplasm_search(...) ), the behavior argument determines how that data is handled. The default behavior is "fork".

  • If behavior =="fork", each object in each page_response.results.data array will be treated as an individual datum.
  • If behavior =="map", the page_response.results.data array from each response will be concatenated into the initial response's data array and the resulting page_response.results object will be considered a single datum. For calls which do not return a data array, the response.results object is always treated as a single datum.

# node.poll(callback) <>

Wether a call is a BrAPI 'asynch' call will be determined automatically from the response. By default, polling occurs every 15 seconds. To vary the polling times, or check the status, one can use this method which is available only on brapi call nodes. The callback function receives the full json response as the only argument. If it returns a non-null value greater than zero, the next poll with occur after that many milliseconds. If multiple poll methods are chained, they will execute in order and the last non-null return value greater than zero will be used as the polling time.

//to poll the allelematrix_search call every 10 seconds
BrAPI("https://www.yambase.org/brapi/v1")
    .data(["1038","4542","45534"])
    .allelematrices_search(function(d){
        return {markerprofileDbId:d};
    }).poll(function(response){
        console.log(response.metadata);
        return 10000;
    });

Non-BrAPI Nodes

# node.map(callback) <>

Creates and returns a child BrAPINode which transforms each datum in a manner similar to Array.prototype.map(). The callback function is called with two arguments (datum, key) for each datum, and may return any value which will be passed to child nodes as a single datum.

# node.fork(node [, node, ...]) <>

Creates and returns a child BrAPINode which transforms each datum in a manner similar to node.map(), however, the callback function should return an array. Each item of each returned array will be passed to child nodes as a datum.

# node.filter(callback) <>

Creates and returns a child BrAPINode which transforms each datum in a manner similar to Array.prototype.filter(). The callback function is called with two arguments (datum, key) for each datum, returning true will pass the datum to child nodes while returning false will remove it from the dataflow.

# node.reduce(callback [, initialValue]) <>

Creates and returns a child BrAPINode which transforms each datum in a manner similar to Array.prototype.reduce(). The callback function is called with two arguments (accumulator, datum) and should return the modified accumulator. The result from the reduction will be passed to child nodes as a single datum.

# node.join(node [, node, ...]) <>

Creates and returns a child BrAPINode which takes the output datum from multiple BrAPINodes and joins them into arrays on their keys which are then passed to child nodes. Datum are assigned keys in two different manners. First, when a key-modifying node (a fork node, a reduce node, or a BrAPI node with the behavior of "fork" or called with a single parameter object) transforms data, it will assign each datum a new key in order to maintain uniqueness. Second, keys can be manually assigned using node.keys(...) to create a keys node. node.join(...) will only join nodes which share their most recent key-modifying ancestor, or which have all had their keys manually assigned.

# node.keys(callback) <>

Creates and returns a child BrAPINode which transforms each datum in a manner similar to node.map(). However, instead of modifying the data, it will modify the key for each datum. The callback function is called with two arguments (datum, currentKey) and should return a new key to replace currentKey. Keys returned by the callback function may be any value. If the callback function returns any type other than a string, it will converted to a string before being used as a key. A single key string should not be assigned to two separate datum, doing so will result in errors later in the dataflow.

Accessing Data Output

# node.each(callback) <>

This method registers a callback function which is called each time the node completes the transformation of a datum. The callback function is called with the arguments (datum, key).

# node.all(callback) <>

This method registers a callback function which is called once a node has loaded all data. The callback function is called with a single argument data, which is an array of all data sorted by their keys lexicographically.

Available BrAPI Methods

BrAPI.js Method BrAPI Call (<= v1.3) BrAPI Call (v2.0) Default HTTPMethod
node.allelematrices_search(params,...) /allelematrices-search(>=v1.2) or /allelematrix-search(<v1.2) POST
node.allelematrices(params,...) /allelematrices GET
node.attributes_categories(params,...) /attributes_categories /attributes/categories GET
node.attributes_detail(params,...) /attributes/{attributeDbId} GET
node.attributes_modify(params,...) /attributes/{attributeDbId} PUT
node.attributes_store(params,...) /attributes POST
node.attributes(params,...) /attributes /attributes GET
node.attributevalues_detail(params,...) /attributevalues/{attributeValueDbId} GET
node.attributevalues_modify(params,...) /attributevalues/{attributeValueDbId} PUT
node.attributevalues_store(params,...) /attributevalues POST
node.attributevalues(params,...) /attributevalues GET
node.breedingmethods_detail(params,...) /breedingmethods/{breedingMethodDbId} /breedingmethods/{breedingMethodDbId} GET
node.breedingmethods(params,...) /breedingmethods /breedingmethods GET
node.calls(params,...) /calls (server info) GET
node.serverinfo(params,...) /serverinfo GET
node.calls(params,...) /calls (genotyping calls) GET
node.callsets_calls(params,...) /callsets/{callSetDbId}/calls GET
node.callsets_detail(params,...) /callsets/{callSetDbId} GET
node.callsets(params,...) /callsets GET
node.commoncropnames(params,...) /commoncropnames(>=v1.2) or /crops(<v1.2) /commoncropnames GET
node.crosses_modify(params,...) /crosses PUT
node.crosses_store(params,...) /crosses POST
node.crosses(params,...) /crosses GET
node.crossingprojects_detail(params,...) /crossingprojects/{crossingProjectDbId} GET
node.crossingprojects_modify(params,...) /crossingprojects/{crossingProjectDbId} PUT
node.crossingprojects_store(params,...) /crossingprojects POST
node.crossingprojects(params,...) /crossingprojects GET
node.events(params,...) /events GET
node.germplasm_attributes(params,...) /germplasm/{germplasmDbId}/attributes GET
node.germplasm_detail(params,...) /germplasm/{germplasmDbId} /germplasm/{germplasmDbId} GET
node.germplasm_markerprofiles(params,...) /germplasm/{germplasmDbId}/markerprofiles GET
node.germplasm_mcpd(params,...) /germplasm/{germplasmDbId}/mcpd GET
node.germplasm_modify(params,...) /germplasm/{germplasmDbId} PUT
node.germplasm_pedigree(params,...) /germplasm/{germplasmDbId}/pedigree /germplasm/{germplasmDbId}/pedigree GET
node.germplasm_progeny(params,...) /germplasm/{germplasmDbId}/progeny /germplasm/{germplasmDbId}/progeny GET
node.germplasm_search(params,...) /germplasm-search /search/germplasm POST-->GET
node.germplasm_store(params,...) /germplasm POST
node.germplasm(params,...) /germplasm /germplasm GET
node.images_detail(params,...) /images/{imageDbId} /images/{imageDbId} GET
node.images_imagecontent(params,...) /images/{imageDbId}/imagecontent PUT
node.images_imagecontent_modify(params,...) /images/{imageDbId}/imagecontent PUT
node.images_modify(params,...) /images/{imageDbId} PUT
node.images_store(params,...) /images POST
node.images(params,...) /images /images GET
node.lists_detail(params,...) /lists/{listDbId} /lists/{listDbId} GET
node.lists_modify(params,...) /lists/{listDbId} PUT
node.lists_items_store(params,...) /lists/{listDbId}/items POST
node.lists_store(params,...) /lists POST
node.lists(params,...) /lists /lists GET
node.locations_detail(params,...) /locations/{locationDbId} /locations/{locationDbId} GET
node.locations_modify(params,...) /locations/{locationDbId} PUT
node.locations_store(params,...) /locations POST
node.locations(params,...) /locations /locations GET
node.maps_detail(params,...) /maps/{mapDbId} /maps/{mapDbId} GET
node.maps_linkagegroups_detail(params,...) /maps/{mapsDbId}/positions/{linkageGroupId} GET
node.maps_linkagegroups(params,...) /maps/{mapDbId}/linkagegroups GET
node.maps_positions(params,...) /maps/{mapsDbId}/positions GET
node.maps(params,...) /maps /maps GET
node.markerpositions(params,...) /markerpositions GET
node.markerprofiles_detail(params,...) /markerprofiles/{markerprofileDbId} GET
node.markerprofiles_search(params,...) /markerprofiles-search POST
node.markerprofiles(params,...) /markerprofiles GET
node.markers_detail(params,...) /markers/{markerDbId} GET
node.markers_search(params,...) /markers-search POST
node.markers(params,...) /markers GET
node.methods_detail(params,...) /methods/{methodDbId} /methods/{methodDbId} GET
node.methods_modify(params,...) /methods/{methodDbId} PUT
node.methods_store(params,...) /methods POST
node.methods(params,...) /methods /methods GET
node.observationlevels(params,...) /observationlevels(>=v1.2) or /observationLevels(<v1.2) /observationlevels GET
node.observations_modify(params,...) /observations/{observationDbId} PUT
node.observations_detail(params,...) /observations/{observationDbId} GET
node.observations_modify_multiple(params,...) /observations PUT
node.observations_store(params,...) /observations POST
node.observations_table(params,...) /observations/table GET
node.observations(params,...) /observations GET
node.observationunits_modify(params,...) /observationunits/{observationUnitDbId} PUT
node.observationunits_detail(params,...) /observationunits/{observationUnitDbId} GET
node.observationunits_modify_multiple(params,...) /observationunits PUT
node.observationunits_store(params,...) /observationunits POST
node.observationunits_table(params,...) /observationunits/table GET
node.observationunits(params,...) /observationunits /observationunits GET
node.ontologies(params,...) /ontologies /ontologies GET
node.people_detail(params,...) /people/{personDbId} /people/{personDbId} GET
node.people_modify(params,...) /people/{personDbId} PUT
node.people_store(params,...) /people POST
node.people(params,...) /people /people GET
node.phenotypes_search_csv(params,...) /phenotypes-search/csv POST
node.phenotypes_search_table(params,...) /phenotypes-search/table POST
node.phenotypes_search_tsv(params,...) /phenotypes-search/tsv POST
node.phenotypes_search(params,...) /phenotypes-search POST
node.phenotypes(params,...) /phenotypes POST
node.plannedcrosses_modify(params,...) /plannedcrosses PUT
node.plannedcrosses_store(params,...) /plannedcrosses POST
node.plannedcrosses(params,...) /plannedcrosses GET
node.programs_detail(params,...) /programs/{programDbId} GET
node.programs_modify(params,...) /programs/{programDbId} PUT
node.programs_store(params,...) /programs POST
node.programs(params,...) /programs /programs GET
node.references_bases(params,...) /references/{referenceDbId}/bases GET
node.references_detail(params,...) /references/{referenceDbId} GET
node.references(params,...) /references GET
node.referencesets_detail(params,...) /referencesets/{referenceSetDbId} GET
node.referencesets(params,...) /referencesets GET
node.samples_detail(params,...) /samples/{sampleId} /samples/{sampleDbId} GET
node.samples_modify(params,...) /samples/{sampleDbId} PUT
node.samples_store(params,...) /samples POST
node.samples(params,...) /samples /samples GET
node.scales_detail(params,...) /scales/{scaleDbId} /scales/{scaleDbId} GET
node.scales_modify(params,...) /scales/{scaleDbId} PUT
node.scales_store(params,...) /scales POST
node.scales(params,...) /scales /scales GET
node.search_attributes(params,...) /search/attributes POST-->GET
node.search_attributevalues(params,...) /search/attributevalues POST-->GET
node.search_calls(params,...) /search/calls POST-->GET
node.search_callsets(params,...) /search/callsets POST-->GET
node.search_germplasm(params,...) /germplasm-search /search/germplasm POST-->GET
node.search_GET(entity, params,...) /search/{entity}/{searchResultDbId} /search/{entity}/{searchResultsDbId} GET
node.search_images(params,...) /search/images /search/images POST-->GET
node.search_lists(params,...) /search/lists POST-->GET
node.search_locations(params,...) /search/locations POST-->GET
node.search_markerpositions(params,...) /search/markerpositions POST-->GET
node.search_markers(params,...) /markers-search /search/markers POST-->GET
node.search_observations(params,...) /search/observations POST-->GET
node.search_observationtables(params,...) /search/observationtables POST-->GET
node.search_observationunits(params,...) /search/observationunits /search/observationunits POST-->GET
node.search_people(params,...) /search/people POST-->GET
node.search_POST(entity, params,...) /search/{entity} POST
node.search_programs(params,...) /programs-search /search/programs POST-->GET
node.search_references(params,...) /search/references POST-->GET
node.search_referencesets(params,...) /search/referencesets POST-->GET
node.search_samples(params,...) /samples-search /search/samples POST-->GET
node.search_studies(params,...) /studies-search /search/studies POST-->GET
node.search_trials(params,...) /search/trials POST-->GET
node.search_variants(params,...) /search/variants POST-->GET
node.search_variantsets(params,...) /search/variantsets POST-->GET
node.search(entity, params,...) /search/{entity-->search/{entity}/{searchResultDbId} /search/{entity-->search/{entity}/{searchResultsDbId} POST-->GET
node.seasons_detail(params,...) /seasons/{seasonDbId} GET
node.seasons_modify(params,...) /seasons/{seasonDbId} PUT
node.seasons_store(params,...) /seasons POST
node.seasons(params,...) /seasons /seasons GET
node.seedlots_detail_transactions(params,...) /seedlots/{seedLotDbId}/transactions GET
node.seedlots_detail(params,...) /seedlots/{seedLotDbId} GET
node.seedlots_modify(params,...) /seedlots/{seedLotDbId} PUT
node.seedlots_store(params,...) /seedlots POST
node.seedlots_transactions_store(params,...) /seedlots/transactions POST
node.seedlots_transactions(params,...) /seedlots/transactions GET
node.seedlots(params,...) /seedlots GET
node.studies_detail(params,...) /studies/{studyDbId} /studies/{studyDbId} GET
node.studies_germplasm(params,...) /studies/{studyDbId}/germplasm GET
node.studies_layouts(params,...) /studies/{studyDbId}/layouts /studies/{studyDbId}/layout GET
node.studies_modify(params,...) /studies/{studyDbId} PUT
node.studies_observations_modify(params,...) PUT /studies/{studyDbId}/observations(>=v1.1) or /studies/{studyDbId}/observations(<v1.1) POST
node.studies_observations_zip(params,...) /studies/{studyDbId}/observations/zip POST
node.studies_observations(params,...) /studies/{studyDbId}/observations GET
node.studies_observationvariables(params,...) /studies/{studyDbId}/observationvariables GET
node.studies_store(params,...) /studies POST
node.studies_table_add(params,...) /studies/{studyDbId}/table POST
node.studies_table(params,...) /studies/{studyDbId}/table GET
node.studies(params,...) /studies /studies GET
node.studytypes(params,...) /studytypes(>=v1.1) or /studyTypes(<v1.1) /studytypes GET
node.traits_detail(params,...) /traits/{traitDbId} /traits/{traitDbId} GET
node.traits_modify(params,...) /traits/{traitDbId} PUT
node.traits_store(params,...) /traits POST
node.traits(params,...) /traits /traits GET
node.trials_detail(params,...) /trials/{trialDbId} /trials/{trialDbId} GET
node.trials_modify(params,...) /trials/{trialDbId} PUT
node.trials_store(params,...) /trials POST
node.trials(params,...) /trials /trials GET
node.variables_datatypes(params,...) /variables/datatypes GET
node.variables_detail(params,...) /variables/{observationVariableDbId} /variables/{observationVariableDbId} GET
node.variables_modify(params,...) /variables/{observationVariableDbId} PUT
node.variables_search(params,...) /variables-search /search/variables POST
node.variables_store(params,...) /variables POST
node.variables(params,...) /variables /variables GET
node.variants_calls(params,...) /variantsets/{variantSetDbId}/calls GET
node.variants_detail(params,...) /variants/{variantDbId} GET
node.variants(params,...) /variants GET
node.variantsets_calls(params,...) /variants/{variantDbId}/calls GET
node.variantsets_callsets(params,...) /variantsets/{variantSetDbId}/callsets GET
node.variantsets_detail(params,...) /variantsets/{variantSetDbId} GET
node.variantsets_extract_store(params,...) /variantsets/extract POST
node.variantsets_variants(params,...) /variantsets/{variantSetDbId}/variants GET
node.variantsets(params,...) /variantsets GET
node.vendor_orders_plates(params,...) /vendor/orders/{orderId}/plates /vendor/orders/{orderId}/plates GET
node.vendor_orders_results(params,...) /vendor/orders/{orderId}/results /vendor/orders/{orderId}/results GET
node.vendor_orders_status(params,...) /vendor/orders/{orderId}/status /vendor/orders/{orderId}/status GET
node.vendor_orders_store(params,...) /vendor/orders POST
node.vendor_orders(params,...) /vendor/orders /vendor/orders GET
node.vendor_plates_detail(params,...) /vendor/plates/{submissionId} /vendor/plates/{submissionId} GET
node.vendor_plates_search(params,...) /vendor/plates-search(>=v1.2) or /vendor/plate-search(<v1.2) POST
node.vendor_plates(params,...) /vendor/plates /vendor/plates POST
node.vendor_specifications(params,...) /vendor/specifications /vendor/specifications GET