diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index f080f8b3..00000000 --- a/.editorconfig +++ /dev/null @@ -1,24 +0,0 @@ -# EditorConfig is awesome: http://EditorConfig.org - -# top-most EditorConfig file -root = true - -# Unix-style newlines with a newline ending every file -[*] -end_of_line = lf -insert_final_newline = true - -[*.js] -# Avoid trailing whitespace -trim_trailing_whitespace = true -# Set default charset for JS files -charset = utf-8 -# Tab indentation -indent_style = tab -tab_width = 2 - -# Matches the exact files either package.json or .travis.yml -# and overrides them to use 2-space indentation -[{package.json,.travis.yml}] -indent_style = space -indent_size = 2 diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 3e811fa8..00000000 --- a/.eslintignore +++ /dev/null @@ -1,13 +0,0 @@ -# /node_modules/* and /bower_components/* in the project root are ignored by default - -# Ignore built files -browser/ - -# Ignore generated coverage directory -coverage/ - -# Ignore documentation site scaffold -documentation/ - -# Ignore local testing script -test.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index fe28b0d0..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,76 +0,0 @@ -module.exports = { - 'env': { - 'es6': true, - 'node': true, - }, - 'extends': 'eslint:recommended', - 'parserOptions': { - 'ecmaVersion': 2018, - }, - 'rules': { - 'array-bracket-spacing': [ 'error', 'always' ], - 'arrow-parens': [ 'error', 'as-needed', { 'requireForBlockBody': true } ], - 'arrow-spacing': [ 'error', { - 'before': true, - 'after': true, - } ], - 'block-spacing': [ 'error' ], - 'comma-dangle': [ 'error', 'always-multiline' ], - 'comma-spacing': [ 'error', { - 'before': false, - 'after': true, - } ], - 'eol-last': [ 'error', 'unix' ], - 'eqeqeq': [ 'error' ], - 'func-call-spacing': [ 'error' ], - 'indent': [ 'error', 'tab' ], - 'key-spacing': [ 'error', { - 'beforeColon': false, - 'afterColon': true, - } ], - 'linebreak-style': [ 'error', 'unix' ], - 'no-console': [ 'warn' ], - 'no-mixed-spaces-and-tabs': [ 'error', 'smart-tabs' ], - 'no-multiple-empty-lines': [ 'error', { - 'max': 1, - } ], - 'no-var': [ 'error' ], - 'object-curly-newline': [ 'error', { - 'ObjectExpression': { - 'consistent': true, - 'minProperties': 2, - 'multiline': true, - }, - 'ObjectPattern': { - 'consistent': true, - 'multiline': true, - }, - 'ImportDeclaration': { - 'consistent': true, - 'multiline': true, - }, - 'ExportDeclaration': { - 'consistent': true, - 'minProperties': 2, - 'multiline': true, - }, - } ], - 'object-curly-spacing': [ 'error', 'always' ], - 'object-property-newline': [ 'error' ], - 'prefer-arrow-callback': [ 'error' ], - 'prefer-const': [ 'error' ], - 'quotes': [ 'error', 'single' ], - 'semi': [ 'error', 'always' ], - 'semi-spacing': [ 'error', { - 'before': false, - 'after': true, - } ], - 'space-before-function-paren': [ 'error', { - 'anonymous': 'never', - 'asyncArrow': 'always', - 'named': 'never', - } ], - 'space-in-parens': [ 'error', 'always' ], - 'yoda': [ 'error', 'never' ], - }, -}; diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1b2bbcab..00000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -./**/*.json -diff diff --git a/.gitignore b/.gitignore index b4170884..709822ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,41 @@ -# Logs +# This is the gitignore for the gh-pages branch! +# It excludes all of the normal master branch's working files, so that +# when you switch back and forth it is easy to commit only docs changes. + +# Files that belong to the main source code branches: +docs-theme/ +documentation/ +build/ +lib/ +tests/ +.jscsrc +.jshintrc +Gruntfile.js +LICENSE +package.json +README.md +wp.js + +# Files that are excluded in the .gitignore on master (because if we don't need +# them there we probably don't need them here either, excepting docs/): logs -*.log - -# Runtime data pids *.pid *.seed - -# Directory for instrumented libs generated by jscoverage/JSCover lib-cov - -# Coverage directory used by tools like istanbul coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -# Deployed apps should consider commenting this line out: -# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git node_modules - test.js - -# Docs directory should not be committed into master: -# The rendered API docs files will live in the gh-pages branch -docs/ -docs-tmp/ -index.html -404.html - -# Dynamic content within Jekyll documentation site -documentation/*.md -documentation/*.zip -documentation/index.html *.ignore -# Local editor/IDE files & environment configuration -.idea -.vscode -*.sublime-* -.ruby-version +# Jekyll .gitignore from master: +_site +.sass-cache +.jekyll-metadata -# Built files (for use in browser) -browser/ +*.lock -# Consuming applications should maintain their own lockfile. -package-lock.json +# Files important only to the generation of the site: +*.combyne +docs-tmp/ diff --git a/.jsdoc.json b/.jsdoc.json deleted file mode 100644 index 7451b167..00000000 --- a/.jsdoc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "source": { - "include": [ - "lib", - "wpapi.js", - "wpapi-computed-methods.jsdoc", - "package.json", - "build/api-reference-readme.md" - ], - "includePattern": ".+\\.js(doc)?$", - "excludePattern": "(browser/|bin/|build/|data/|tests/|node_modules)" - }, - "plugins": [ - "plugins/markdown" - ], - "tags": { - "dictionaries": ["jsdoc"] - }, - "templates": { - "useLongnameInNav": false, - "showInheritedInNav": true - }, - "opts": { - "destination": "./documentation/api-reference/", - "template": "node_modules/minami", - "encoding": "utf8", - "recurse": true - } -} diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 3eeb1efc..00000000 --- a/.npmignore +++ /dev/null @@ -1,17 +0,0 @@ -# Logs -logs -*.log -pids -*.pid -*.seed -.idea -*.sublime-* -webpack.* -_gh_issues/ -build/ -coverage/ -docs-tmp/ -documentation/ -index.html -test.js -tests/ diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..2bf1c1cc --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.3.1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eec9a381..00000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: node_js -script: "npm run test:ci" -node_js: - - "node" - - "lts/*" - - "10" - - "8" -# Opt-in to travis container infrastructure -sudo: false diff --git a/01-installation.md b/01-installation.md new file mode 100644 index 00000000..6c4224b0 --- /dev/null +++ b/01-installation.md @@ -0,0 +1,30 @@ +--- +layout: page +title: Installation +permalink: /installation/ +--- + +* TOC +{:toc} + +`node-wpapi` works both on the server or in the browser. Node.js version 4.0 or higher is required. + +### Install with NPM + +To use the library from Node, install it with [npm](http://npmjs.org): + +```bash +npm install --save wpapi +``` + +Then, within your application's script files, `require` the module to gain access to it: + +```javascript +var WPAPI = require( 'wpapi' ); +``` + +This library is designed to work in the browser as well, via a build system such as Browserify or Webpack; just install the package and `require( 'wpapi' )` from your application code. + +### Download the UMD Bundle + +Alternatively, you may download a [ZIP archive of the bundled library code](https://wp-api.github.io/node-wpapi/wpapi.zip). These files are UMD modules, which may be included directly on a page using a regular ` + + + + + + + + + + + + + + + +
+ +

WPAPI

+ + + + + + + +
+ +
+ +

+ WPAPI +

+ + +
+ +
+
+ + +
+ + + +

new WPAPI(options)

+ + + + + +
+

Construct a REST API client instance object to create

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
options + + +Object + + + + +

An options hash to configure the instance

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
endpoint + + +String + + + + + + + + + + +

The URI for a WP-API endpoint

+ +
username + + +String + + + + + + <optional>
+ + + + + +
+

A WP-API Basic Auth username

+ +
password + + +String + + + + + + <optional>
+ + + + + +
+

A WP-API Basic Auth password

+ +
nonce + + +String + + + + + + <optional>
+ + + + + +
+

A WP nonce for use with cookie authentication

+ +
routes + + +Object + + + + + + <optional>
+ + + + + +
+

A dictionary of API routes with which to + bootstrap the WPAPI instance: the instance will + be initialized with default routes only + if this property is omitted

+ +
transport + + +String + + + + + + <optional>
+ + + + + +
+

An optional dictionary of HTTP transport + methods (.get, .post, .put, .delete, .head) + to use instead of the defaults, e.g. to use + a different HTTP library than superagent

+ +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+

(static) .transport :Object

+ + + + +
+

Default HTTP transport methods object for all WPAPI instances

+

These methods may be extended or replaced on an instance-by-instance basis

+
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
transport + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +Object + + +
  • +
+ + + + + +
+ + + +
+

#setHeaders

+ + + + +
+

Set the default headers to use for all HTTP requests created from this WPAPI +site instance. Accepts a header name and its associated value as two strings, +or multiple headers as an object of name-value pairs.

+
+ + + + + +
+ + + + +
Since:
+
  • 1.1.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
Examples
+ +

Set a single header to be used by all requests to this site

+ +
    site.setHeaders( 'Authorization', 'Bearer trustme' )...
+ +

Set multiple headers to be used by all requests to this site

+ +
    site.setHeaders({
+      Authorization: 'Bearer comeonwereoldfriendsright',
+      'Accept-Language': 'en-CA'
+    })...
+ + +
+ + + + + +

Methods

+ + + +
+ + + +

(static) .categories() → {WPRequest}

+ + + + + +
+

Start a request against /categories endpoint

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .comments() → {WPRequest}

+ + + + + +
+

Start a request against /comments endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .discover(url) → {Promise}

+ + + + + +
+

Take an arbitrary WordPress site, deduce the WP REST API root endpoint, query +that endpoint, and parse the response JSON. Use the returned JSON response +to instantiate a WPAPI instance bound to the provided site.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url + + +string + + + + +

A URL within a REST API-enabled WordPress website

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise that resolves to a configured WPAPI instance bound +to the deduced endpoint, or rejected if an endpoint is not found or the +library is unable to parse the provided endpoint.

+
+ + +
+ + + +
+ + +
+ + + +

(static) .media() → {WPRequest}

+ + + + + +
+

Start a request against /media endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .pages() → {WPRequest}

+ + + + + +
+

Start a request against /pages endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .posts() → {WPRequest}

+ + + + + +
+

Start a request against /posts endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .settings() → {WPRequest}

+ + + + + +
+

Start a request against /settings endpoint

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .site(endpoint, routes) → {WPAPI}

+ + + + + +
+

Convenience method for making a new WPAPI instance

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
endpoint + + +String + + + + +

The URI for a WP-API endpoint

+ +
routes + + +Object + + + + +

The "routes" object from the JSON object returned + from the root API endpoint of a WP site, which should + be a dictionary of route definition objects keyed by + the route's regex pattern

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPAPI + + +
+
+ + +
+

A new WPAPI instance, bound to the provided endpoint

+
+ + +
+ + + +
+
Examples
+ +
These are equivalent:
+
+    var wp = new WPAPI({ endpoint: 'http://my.blog.url/wp-json' });
+    var wp = WPAPI.site( 'http://my.blog.url/wp-json' );
+
+`WPAPI.site` can take an optional API root response JSON object to use when
+bootstrapping the client's endpoint handler methods: if no second parameter
+is provided, the client instance is assumed to be using the default API
+with no additional plugins and is initialized with handlers for only those
+default API routes.
+ +
These are equivalent:
+
+    // {...} means the JSON output of http://my.blog.url/wp-json
+    var wp = new WPAPI({
+      endpoint: 'http://my.blog.url/wp-json',
+      json: {...}
+    });
+    var wp = WPAPI.site( 'http://my.blog.url/wp-json', {...} );
+ +
+ +
+ + +
+ + + +

(static) .statuses() → {WPRequest}

+ + + + + +
+

Start a request against /statuses endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .tags() → {WPRequest}

+ + + + + +
+

Start a request against /tags endpoint

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .taxonomies() → {WPRequest}

+ + + + + +
+

Start a request against /taxonomies endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .transport(transport) → {WPAPI}

+ + + + + +
+

Set custom transport methods to use when making HTTP requests against the API

+

Pass an object with a function for one or many of "get", "post", "put", +"delete" and "head" and that function will be called when making that type +of request. The provided transport functions should take a WPRequest handler +instance (e.g. the result of a wp.posts()... chain or any other chaining +request handler) as their first argument; a data object as their second +argument (for POST, PUT and DELETE requests); and an optional callback as +their final argument. Transport methods should invoke the callback with the +response data (or error, as appropriate), and should also return a Promise.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
transport + + +Object + + + + +

A dictionary of HTTP transport methods

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
get + + +function + + + + + + <optional>
+ + + + + +
+

The function to use for GET requests

+ +
post + + +function + + + + + + <optional>
+ + + + + +
+

The function to use for POST requests

+ +
put + + +function + + + + + + <optional>
+ + + + + +
+

The function to use for PUT requests

+ +
delete + + +function + + + + + + <optional>
+ + + + + +
+

The function to use for DELETE requests

+ +
head + + +function + + + + + + <optional>
+ + + + + +
+

The function to use for HEAD requests

+ +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPAPI + + +
+
+ + +
+

The WPAPI instance, for chaining

+
+ + +
+ + + +
+
Example
+ +

showing how a cache hit (keyed by URI) could short-circuit a get request

+ +
    var site = new WPAPI({
+      endpoint: 'http://my-site.com/wp-json'
+    });
+
+    // Overwrite the GET behavior to inject a caching layer
+    site.transport({
+      get: function( wpreq, cb ) {
+        var result = cache[ wpreq ];
+        // If a cache hit is found, return it via the same callback/promise
+        // signature as the default transport method
+        if ( result ) {
+          if ( cb && typeof cb === 'function' ) {
+            cb( null, result );
+          }
+          return Promise.resolve( result );
+        }
+
+        // Delegate to default transport if no cached data was found
+        return WPAPI.transport.get( wpreq, cb ).then(function( result ) {
+          cache[ wpreq ] = result;
+          return result;
+        });
+      }
+    });
+
+This is advanced behavior; you will only need to utilize this functionality
+if your application has very specific HTTP handling or caching requirements.
+Refer to the "http-transport" module within this application for the code
+implementing the built-in transport methods.
+ +
+ +
+ + +
+ + + +

(static) .types() → {WPRequest}

+ + + + + +
+

Start a request against /types endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) .users() → {WPRequest}

+ + + + + +
+

Start a request against /users endpoints

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + + +
+ + + +
+ + +
+ + + +

#auth(credentials) → {WPAPI}

+ + + + + +
+

Set the authentication to use for a WPAPI site handler instance. Accepts basic +HTTP authentication credentials (string username & password) or a Nonce (for +cookie authentication) by default; may be overloaded to accept OAuth credentials +in the future.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
credentials + + +Object + + + + +

An authentication credentials object

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
username + + +String + + + + + + <optional>
+ + + + + +
+

A WP-API Basic HTTP Authentication username

+ +
password + + +String + + + + + + <optional>
+ + + + + +
+

A WP-API Basic HTTP Authentication password

+ +
nonce + + +String + + + + + + <optional>
+ + + + + +
+

A WP nonce for use with cookie authentication

+ +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPAPI + + +
+
+ + +
+

The WPAPI site handler instance, for chaining

+
+ + +
+ + + +
+
Examples
+ +

Basic Authentication

+ +
    site.auth({
+      username: 'admin',
+      password: 'securepass55'
+    })...
+ +

Cookie/Nonce Authentication

+ +
    site.auth({
+      nonce: 'somenonce'
+    })...
+ +
+ +
+ + +
+ + + +

#bootstrap(routes) → {WPAPI}

+ + + + + +
+

Deduce request methods from a provided API root JSON response object's +routes dictionary, and assign those methods to the current instance. If +no routes dictionary is provided then the instance will be bootstrapped +with route handlers for the default API endpoints only.

+

This method is called automatically during WPAPI instance creation.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
routes + + +Object + + + + +

The "routes" object from the JSON object returned + from the root API endpoint of a WP site, which should + be a dictionary of route definition objects keyed by + the route's regex pattern

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPAPI + + +
+
+ + +
+

The bootstrapped WPAPI client instance (for chaining or assignment)

+
+ + +
+ + + +
+ + +
+ + + +

#namespace(namespace) → {Object}

+ + + + + +
+

Access API endpoint handlers from a particular API namespace object

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
namespace + + +string + + + + +

A namespace string

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Object + + +
+
+ + +
+

An object of route endpoint handler methods for the +routes within the specified namespace

+
+ + +
+ + + +
+
Example
+ +
wp.namespace( 'myplugin/v1' ).author()...
+
+    // Default WP endpoint handlers are assigned to the wp instance itself.
+    // These are equivalent:
+    wp.namespace( 'wp/v2' ).posts()...
+    wp.posts()...
+ +
+ +
+ + +
+ + + +

registerRoute(namespace, restBase, optionsopt) → {function}

+ + + + + +
+

Create and return a handler for an arbitrary WP REST API endpoint.

+

The first two parameters mirror register_rest_route in the REST API +codebase:

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
namespace + + +string + + + + + + + + + + +

A namespace string, e.g. 'myplugin/v1'

+ +
restBase + + +string + + + + + + + + + + +

A REST route string, e.g. '/author/(?P\d+)'

+ +
options + + +object + + + + + + <optional>
+ + + + + +
+

An (optional) options object

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
mixins + + +object + + + + + + <optional>
+ + + + + +
+

A hash of functions to apply as mixins

+ +
methods + + +Array.<string> + + + + + + <optional>
+ + + + + +
+

An array of methods to whitelist (on the leaf node only)

+ +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +function + + +
+
+ + +
+

An endpoint handler factory function for the specified route

+
+ + +
+ + + +
+ + +
+ + + +

#root(relativePathopt) → {WPRequest}

+ + + + + +
+

Generate a query against an arbitrary path on the current endpoint. This is useful for +requesting resources at custom WP-API endpoints, such as WooCommerce's /products.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
relativePath + + +String + + + + + + <optional>
+ + + + + +
+

An endpoint-relative path to which to bind the request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

A request object

+
+ + +
+ + + +
+ + +
+ + + +

#url(url) → {WPRequest}

+ + + + + +
+

Generate a request against a completely arbitrary endpoint, with no assumptions about +or mutation of path, filtering, or query parameters. This request is not restricted to +the endpoint specified during WPAPI object instantiation.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
url + + +String + + + + +

The URL to request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

A WPRequest object bound to the provided URL

+
+ + +
+ + + +
+
Example
+ +
Generate a request to the explicit URL "http://your.website.com/wp-json/some/custom/path"
+
+    wp.url( 'http://your.website.com/wp-json/some/custom/path' ).get()...
+ +
+ +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/WPRequest.html b/api-reference/wpapi/1.1.2/WPRequest.html new file mode 100644 index 00000000..46f0f4c5 --- /dev/null +++ b/api-reference/wpapi/1.1.2/WPRequest.html @@ -0,0 +1,4702 @@ + + + + + + WPRequest - Documentation + + + + + + + + + + + + + + + + + +
+ +

WPRequest

+ + + + + + + +
+ +
+ +

+ WPRequest +

+ + +
+ +
+
+ + +
+ + + +

new WPRequest(options)

+ + + + + +
+

WPRequest is the base API request object constructor

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
options + + +Object + + + + +

A hash of options for the WPRequest instance

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
endpoint + + +String + + + + + + + + + + +

The endpoint URI for the invoking WPAPI instance

+ +
transport + + +Object + + + + + + + + + + +

An object of http transport methods (get, post, etc)

+ +
username + + +String + + + + + + <optional>
+ + + + + +
+

A username for authenticating API requests

+ +
password + + +String + + + + + + <optional>
+ + + + + +
+

A password for authenticating API requests

+ +
nonce + + +String + + + + + + <optional>
+ + + + + +
+

A WP nonce for use with cookie authentication

+ +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

auth(credentials) → {WPRequest}

+ + + + + +
+

Set a request to use authentication, and optionally provide auth credentials

+

If auth credentials were already specified when the WPAPI instance was created, calling +.auth on the request chain will set that request to use the existing credentials:

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
credentials + + +Object + + + + +

An object with 'username' and 'password' string + properties, or else a 'nonce' property

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
username + + +String + + + + + + <optional>
+ + + + + +
+

A WP-API Basic HTTP Authentication username

+ +
password + + +String + + + + + + <optional>
+ + + + + +
+

A WP-API Basic HTTP Authentication password

+ +
nonce + + +String + + + + + + <optional>
+ + + + + +
+

A WP nonce for use with cookie authentication

+ +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+
Examples
+ +

use existing credentials

+ +
    request.auth().get...
+
+Alternatively, a username & password (or nonce) can be explicitly passed into `.auth`:
+ +

use explicit basic authentication credentials

+ +
    request.auth({
+      username: 'admin',
+      password: 'super secure'
+    }).get...
+ +

use a nonce for cookie authentication

+ +
    request.auth({
+      nonce: 'somenonce'
+    })...
+ +
+ +
+ + +
+ + + +

context(context) → {WPRequest}

+ + + + + +
+

Set the context of the request. Used primarily to expose private values on a +request object by setting the context to "edit".

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
context + + +String + + + + +

The context to set on the request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

create(data, callbackopt) → {Promise}

+ + + + + +
+

Create the specified resource with the provided data

+

This is the public interface for creating POST requests

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
data + + +Object + + + + + + + + + + +

The data for the POST request

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the POST request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

delete(dataopt, callbackopt) → {Promise}

+ + + + + +
+

Delete the specified resource

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
data + + +Object + + + + + + <optional>
+ + + + + +
+

Data to send along with the DELETE request

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the DELETE request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

edit() → {WPRequest}

+ + + + + +
+

Convenience wrapper for .context( 'edit' )

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

embed() → {WPRequest}

+ + + + + +
+

Return embedded resources as part of the response payload.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

exclude(ids)

+ + + + + +
+

Exclude specific resource IDs in the response collection.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ids + + +Number +| + +Array.<Number> + + + + +

An ID or array of IDs to exclude

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

file(file, nameopt) → {WPRequest}

+ + + + + +
+

Specify a file or a file buffer to attach to the request, for use when +creating a new Media item

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
file + + +string +| + +object + + + + + + + + + + +

A path to a file (in Node) or an file object + (Node or Browser) to attach to the request

+ +
name + + +string + + + + + + <optional>
+ + + + + +
+

An (optional) filename to use for the file

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+
Examples
+ +

within a server context

+ +
    wp.media()
+      // Pass .file() the file system path to a file to upload
+      .file( '/path/to/file.jpg' )
+      .create({})...
+ +

within a browser context

+ +
    wp.media()
+      // Pass .file() the file reference from an HTML file input
+      .file( document.querySelector( 'input[type="file"]' ).files[0] )
+      .create({})...
+ +
+ +
+ + +
+ + + +

get(callbackopt) → {Promise}

+ + + + + +
+

Get (download the data for) the specified resource

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the GET request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

headers(callbackopt) → {Promise}

+ + + + + +
+

Get the headers for the specified resource

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the HEAD request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the header results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

include(ids)

+ + + + + +
+

Include specific resource IDs in the response collection.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ids + + +Number +| + +Array.<Number> + + + + +

An ID or array of IDs to include

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

namespace(namespace) → {WPRequest}

+ + + + + +
+

Set the namespace of the request, e.g. to specify the API root for routes +registered by wp core v2 ("wp/v2") or by any given plugin. Any previously- +set namespace will be overwritten by subsequent calls to the method.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
namespace + + +String + + + + +

A namespace string, e.g. "wp/v2"

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

offset(offsetNumber)

+ + + + + +
+

Set an arbitrary offset to retrieve items from a specific point in a collection.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
offsetNumber + + +Number + + + + +

The number of items by which to offset the response

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

order(direction)

+ + + + + +
+

Change the sort direction of a returned collection

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
direction + + +String + + + + +

The order to use when sorting the response

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+
Example
+ +

order comments chronologically (oldest first)

+ +
    site.comments().order( 'asc' )...
+ +
+ +
+ + +
+ + + +

orderby(field)

+ + + + + +
+

Order a collection by a specific field

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
field + + +String + + + + +

The field by which to order the response

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

page(pageNumber)

+ + + + + +
+

Set the pagination of a request. Use in conjunction with .perPage() for explicit +pagination handling. (The number of pages in a response can be retrieved from the +response's _paging.totalPages property.)

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
pageNumber + + +Number + + + + +

The page number of results to retrieve

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

param(props, valueopt) → {WPRequest}

+ + + + + +
+

Set a parameter to render into the final query URI.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
props + + +String +| + +Object + + + + + + + + + + +

The name of the parameter to set, or an object containing + parameter keys and their corresponding values

+ +
value + + +String +| + +Number +| + +Array + + + + + + <optional>
+ + + + + +
+

The value of the parameter being set

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

perPage(itemsPerPage)

+ + + + + +
+

Set the number of items to be returned in a page of responses.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
itemsPerPage + + +Number + + + + +

The number of items to return in one page of results

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + + + + + + + +
+

Filter results to those matching the specified search terms.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
searchString + + +String + + + + +

A string to search for within post content

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

setHeaders(headers, valueopt) → {WPRequest}

+ + + + + +
+

Specify one or more headers to send with the dispatched HTTP request.

+
+ + + + + +
+ + + + +
Since:
+
  • 1.1.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
headers + + +String +| + +Object + + + + + + + + + + +

The name of the header to set, or an object of + header names and their associated string values

+ +
value + + +String + + + + + + <optional>
+ + + + + +
+

The value of the header being set

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+
Examples
+ +

Set a single header to be used on this request

+ +
    request.setHeaders( 'Authorization', 'Bearer trustme' )...
+ +

Set multiple headers to be used by this request

+ +
    request.setHeaders({
+      Authorization: 'Bearer comeonwereoldfriendsright',
+      'Accept-Language': 'en-CA'
+    })...
+ +
+ +
+ + +
+ + + +

setPathPart(level, val) → {WPRequest}

+ + + + + +
+

Set a component of the resource URL itself (as opposed to a query parameter)

+

If a path component has already been set at this level, throw an error: +requests are meant to be transient, so any re-writing of a previously-set +path part value is likely to be a mistake.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
level + + +Number +| + +String + + + + +

A "level" of the path to set, e.g. "1" or "2"

+ +
val + + +Number +| + +String + + + + +

The value to set at that path part level

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

slug(slug)

+ + + + + +
+

Query a collection for members with a specific slug.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
slug + + +String + + + + +

A post slug (slug), e.g. "hello-world"

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

then(successCallbackopt, failureCallbackopt) → {Promise}

+ + + + + +
+

Calling .then on a query chain will invoke the query as a GET and return a promise

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
successCallback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to handle the data returned from the GET request

+ +
failureCallback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to handle any errors encountered by the request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

toString() → {String}

+ + + + + +
+

Parse the request into a WordPress API request URI string

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +String + + +
+
+ + +
+

The URI for the HTTP request to be sent

+
+ + +
+ + + +
+ + +
+ + + +

validatePath() → {WPRequest}

+ + + + + +
+

Validate whether the specified path parts are valid for this endpoint

+

"Path parts" are non-query-string URL segments, like "some" "path" in the URL +mydomain.com/some/path?and=a&query=string&too. Because a well-formed path +is necessary to execute a successful API request, we throw an error if the +user has omitted a value (such as /some/[missing component]/url) or has +provided a path part value that does not match the regular expression the +API uses to goven that segment.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +WPRequest + + +
+
+ + +
+

The WPRequest instance (for chaining), if no errors were found

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 00000000..5d20d916 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 00000000..3ed7be4b --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 00000000..1205787b Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Bold-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.eot new file mode 100644 index 00000000..1f639a15 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.svg new file mode 100644 index 00000000..6a2607b9 --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 00000000..ed760c06 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-BoldItalic-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.eot new file mode 100644 index 00000000..0c8a0ae0 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.svg new file mode 100644 index 00000000..e1075dcc --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 00000000..ff652e64 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Italic-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.eot new file mode 100644 index 00000000..14868406 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 00000000..11a472ca --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 00000000..e7860748 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Light-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 00000000..8f445929 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 00000000..431d7e35 --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 00000000..43e8b9e6 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-LightItalic-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 00000000..6bbc3cf5 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.svg new file mode 100644 index 00000000..25a39523 --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 00000000..e231183d Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Regular-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.eot new file mode 100644 index 00000000..d8375dd0 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.svg new file mode 100644 index 00000000..eec4db8b --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.ttf b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.ttf new file mode 100644 index 00000000..b3290843 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.ttf differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.woff new file mode 100644 index 00000000..28d6adee Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-Semibold-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.eot b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.eot new file mode 100644 index 00000000..0ab1db22 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.eot differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.svg b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.svg new file mode 100644 index 00000000..7166ec1b --- /dev/null +++ b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.ttf b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.ttf new file mode 100644 index 00000000..d2d6318f Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.ttf differ diff --git a/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.woff b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.woff new file mode 100644 index 00000000..d4dfca40 Binary files /dev/null and b/api-reference/wpapi/1.1.2/fonts/OpenSans-SemiboldItalic-webfont.woff differ diff --git a/api-reference/wpapi/1.1.2/index.html b/api-reference/wpapi/1.1.2/index.html new file mode 100644 index 00000000..9dcfebe6 --- /dev/null +++ b/api-reference/wpapi/1.1.2/index.html @@ -0,0 +1,72 @@ + + + + + + Home - Documentation + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+

WPAPI Code Reference

Welcome to the API Reference for the wpapi NPM package.

+

Running require( 'wpapi' ) returns the WPAPI constructor, which you can read about by clicking its name in the class list in the menu. Each request handler factory on WPAPI (such as .posts(), .pages(), etc.) returns a WPRequest object, conditionally augmented with one or more mixins depending on the capabilities of the associated endpoints.

+

For user guides & tutorials, visit wp-api.org/node-wpapi/.

+
+ + + + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/lib_autodiscovery.js.html b/api-reference/wpapi/1.1.2/lib_autodiscovery.js.html new file mode 100644 index 00000000..0298563a --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_autodiscovery.js.html @@ -0,0 +1,96 @@ + + + + + + lib/autodiscovery.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/autodiscovery.js

+ + + + + + + +
+
+
/**
+ * Utility methods used when querying a site in order to discover its available
+ * API endpoints
+ *
+ * @module autodiscovery
+ */
+'use strict';
+
+var parseLinkHeader = require( 'parse-link-header' );
+
+/**
+ * Attempt to locate a `rel="https://api.w.org"` link relation header
+ *
+ * @method locateAPIRootHeader
+ * @param {Object} response A response object with a link or headers property
+ * @returns {String} The URL of the located API root
+ */
+function locateAPIRootHeader( response ) {
+	// Define the expected link rel value per http://v2.wp-api.org/guide/discovery/
+	var rel = 'https://api.w.org/';
+
+	// Extract & parse the response link headers
+	var link = response.link || ( response.headers && response.headers.link );
+	var headers = parseLinkHeader( link );
+	var apiHeader = headers && headers[ rel ];
+
+	if ( apiHeader && apiHeader.url ) {
+		return apiHeader.url;
+	}
+
+	throw new Error( 'No header link found with rel="https://api.w.org/"' );
+}
+
+module.exports = {
+	locateAPIRootHeader: locateAPIRootHeader
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_constructors_wp-request.js.html b/api-reference/wpapi/1.1.2/lib_constructors_wp-request.js.html new file mode 100644 index 00000000..e555fba9 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_constructors_wp-request.js.html @@ -0,0 +1,831 @@ + + + + + + lib/constructors/wp-request.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/constructors/wp-request.js

+ + + + + + + +
+
+
'use strict';
+
+var qs = require( 'qs' );
+var _unique = require( 'lodash.uniq' );
+var extend = require( 'node.extend' );
+
+var alphaNumericSort = require( '../util/alphanumeric-sort' );
+var keyValToObj = require( '../util/key-val-to-obj' );
+var paramSetter = require( '../util/parameter-setter' );
+var objectReduce = require( '../util/object-reduce' );
+
+/**
+ * WPRequest is the base API request object constructor
+ *
+ * @constructor WPRequest
+ * @param {Object} options A hash of options for the WPRequest instance
+ * @param {String} options.endpoint The endpoint URI for the invoking WPAPI instance
+ * @param {Object} options.transport An object of http transport methods (get, post, etc)
+ * @param {String} [options.username] A username for authenticating API requests
+ * @param {String} [options.password] A password for authenticating API requests
+ * @param {String} [options.nonce] A WP nonce for use with cookie authentication
+ */
+function WPRequest( options ) {
+	/**
+	 * Configuration options for the request
+	 *
+	 * @property _options
+	 * @type Object
+	 * @private
+	 * @default {}
+	 */
+	this._options = [
+		// Whitelisted options keys
+		'auth',
+		'endpoint',
+		'headers',
+		'username',
+		'password',
+		'nonce'
+	].reduce(function( localOptions, key ) {
+		if ( options && options[ key ] ) {
+			localOptions[ key ] = options[ key ];
+		}
+		return localOptions;
+	}, {});
+
+	/**
+	 * The HTTP transport methods (.get, .post, .put, .delete, .head) to use for this request
+	 *
+	 * @property transport
+	 * @type {Object}
+	 * @private
+	 */
+	this.transport = options && options.transport;
+
+	/**
+	 * A hash of query parameters
+	 * This is used to store the values for supported query parameters like ?_embed
+	 *
+	 * @property _params
+	 * @type Object
+	 * @private
+	 * @default {}
+	 */
+	this._params = {};
+
+	/**
+	 * Methods supported by this API request instance:
+	 * Individual endpoint handlers specify their own subset of supported methods
+	 *
+	 * @property _supportedMethods
+	 * @type Array
+	 * @private
+	 * @default [ 'head', 'get', 'put', 'post', 'delete' ]
+	 */
+	this._supportedMethods = [ 'head', 'get', 'put', 'post', 'delete' ];
+
+	/**
+	 * A hash of values to assemble into the API request path
+	 * (This will be overwritten by each specific endpoint handler constructor)
+	 *
+	 * @property _path
+	 * @type Object
+	 * @private
+	 * @default {}
+	 */
+	this._path = {};
+}
+
+// Private helper methods
+// ======================
+
+/**
+ * Identity function for use within invokeAndPromisify()
+ * @private
+ */
+function identity( value ) {
+	return value;
+}
+
+/**
+ * Process arrays of taxonomy terms into query parameters.
+ * All terms listed in the arrays will be required (AND behavior).
+ *
+ * This method will not be called with any values unless we are handling
+ * an endpoint with the filter mixin; however, since parameter handling
+ * (and therefore `_renderQuery()`) are part of WPRequest itself, this
+ * helper method lives here alongside the code where it is used.
+ *
+ * @example
+ *     prepareTaxonomies({
+ *         tag: [ 'tag1 ', 'tag2' ], // by term slug
+ *         cat: [ 7 ] // by term ID
+ *     }) === {
+ *         tag: 'tag1+tag2',
+ *         cat: '7'
+ *     }
+ *
+ * @private
+ * @param {Object} taxonomyFilters An object of taxonomy term arrays, keyed by taxonomy name
+ * @returns {Object} An object of prepareFilters-ready query arg and query param value pairs
+ */
+function prepareTaxonomies( taxonomyFilters ) {
+	if ( ! taxonomyFilters ) {
+		return {};
+	}
+
+	return objectReduce( taxonomyFilters, function( result, terms, key ) {
+		// Trim whitespace and concatenate multiple terms with +
+		result[ key ] = terms.map(function( term ) {
+			// Coerce term into a string so that trim() won't fail
+			return ( term + '' ).trim().toLowerCase();
+		}).join( '+' );
+
+		return result;
+	}, {});
+}
+
+/**
+ * Return an object with any properties with undefined, null or empty string
+ * values removed.
+ *
+ * @example
+ *
+ *     populated({
+ *       a: 'a',
+ *       b: '',
+ *       c: null
+ *     }); // { a: 'a' }
+ *
+ * @private
+ * @param {Object} obj An object of key/value pairs
+ * @returns {Object} That object with all empty values removed
+ */
+function populated( obj ) {
+	if ( ! obj ) {
+		return obj;
+	}
+	return objectReduce( obj, function( values, val, key ) {
+		if ( val !== undefined && val !== null && val !== '' ) {
+			values[ key ] = val;
+		}
+		return values;
+	}, {});
+}
+
+/**
+ * Assert whether a provided URL component is "valid" by checking it against
+ * an array of registered path component validator methods for that level of
+ * the URL path.
+ *
+ * @private
+ * @param {object[]} levelDefinitions An array of Level Definition objects
+ * @param {string}   levelContents    The URL path string that has been specified
+ *                                    for use on the provided level
+ * @returns {boolean} Whether the provided input matches any of the provided
+ * level validation functions
+ */
+function validatePathLevel( levelDefinitions, levelContents ) {
+	// One "level" may have multiple options, as a route tree is a branching
+	// structure. We consider a level "valid" if the provided levelContents
+	// match any of the available validators.
+	var valid = levelDefinitions.reduce(function( anyOptionValid, levelOption ) {
+		if ( ! levelOption.validate ) {
+			// If there is no validator function, the level is implicitly valid
+			return true;
+		}
+		return anyOptionValid || levelOption.validate( levelContents );
+	}, false );
+
+	if ( ! valid ) {
+		throw new Error([
+			'Invalid path component:',
+			levelContents,
+			// awkward pluralization support:
+			'does not match' + ( levelDefinitions.length > 1 ? ' any of' : '' ),
+			levelDefinitions.reduce(function( components, levelOption ) {
+				return components.concat( levelOption.component );
+			}, [] ).join( ', ' )
+		].join( ' ' ) );
+	}
+}
+
+// (Semi-)Private Prototype Methods
+// ================================
+
+/**
+ * Process the endpoint query's filter objects into a valid query string.
+ * Nested objects and Array properties are rendered with indexed array syntax.
+ *
+ * @example
+ *     _renderQuery({ p1: 'val1', p2: 'val2' });  // ?p1=val1&p2=val2
+ *     _renderQuery({ obj: { prop: 'val' } });    // ?obj[prop]=val
+ *     _renderQuery({ arr: [ 'val1', 'val2' ] }); // ?arr[0]=val1&arr[1]=val2
+ *
+ * @private
+ *
+ * @method _renderQuery
+ * @returns {String} A query string representing the specified filter parameters
+ */
+WPRequest.prototype._renderQuery = function() {
+	// Build the full query parameters object
+	var queryParams = extend( {}, populated( this._params ) );
+
+	// Prepare any taxonomies and merge with other filter values
+	var taxonomies = prepareTaxonomies( this._taxonomyFilters );
+	queryParams.filter = extend( {}, populated( this._filters ), taxonomies );
+
+	// Parse query parameters object into a query string, sorting the object
+	// properties by alphabetical order (consistent property ordering can make
+	// for easier caching of request URIs)
+	var queryString = qs.stringify( queryParams, { arrayFormat: 'brackets' } )
+		.split( '&' )
+		.sort()
+		.join( '&' );
+
+	// Check if the endpoint contains a previous query and set the query character accordingly.
+	var queryCharacter = /\?/.test( this._options.endpoint ) ? '&' : '?';
+
+	// Prepend a "?" (or a "&") if a query is present, and return.
+	return ( queryString === '' ) ? '' : queryCharacter + queryString;
+};
+
+/**
+ * Validate & assemble a path string from the request object's _path
+ *
+ * @private
+ * @returns {String} The rendered path
+ */
+WPRequest.prototype._renderPath = function() {
+	// Call validatePath: if the provided path components are not well-formed,
+	// an error will be thrown
+	this.validatePath();
+
+	var pathParts = this._path;
+	var orderedPathParts = Object.keys( pathParts )
+		.sort(function( a, b ) {
+			var intA = parseInt( a, 10 );
+			var intB = parseInt( b, 10 );
+			return intA - intB;
+		})
+		.map(function( pathPartKey ) {
+			return pathParts[ pathPartKey ];
+		});
+
+	// Combine all parts of the path together, filtered to omit any components
+	// that are unspecified or empty strings, to create the full path template
+	var path = [
+		this._namespace
+	].concat( orderedPathParts ).filter( identity ).join( '/' );
+
+	return path;
+};
+
+// Public Prototype Methods
+// ========================
+
+/**
+ * Parse the request into a WordPress API request URI string
+ *
+ * @method
+ * @returns {String} The URI for the HTTP request to be sent
+ */
+WPRequest.prototype.toString = function() {
+	// Render the path to a string
+	var path = this._renderPath();
+
+	// Render the query string
+	var queryStr = this._renderQuery();
+
+	return this._options.endpoint + path + queryStr;
+};
+
+/**
+ * Set a component of the resource URL itself (as opposed to a query parameter)
+ *
+ * If a path component has already been set at this level, throw an error:
+ * requests are meant to be transient, so any re-writing of a previously-set
+ * path part value is likely to be a mistake.
+ *
+ * @method
+ * @chainable
+ * @param {Number|String} level A "level" of the path to set, e.g. "1" or "2"
+ * @param {Number|String} val   The value to set at that path part level
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.setPathPart = function( level, val ) {
+	if ( this._path[ level ] ) {
+		throw new Error( 'Cannot overwrite value ' + this._path[ level ] );
+	}
+	this._path[ level ] = val;
+
+	return this;
+};
+
+/**
+ * Validate whether the specified path parts are valid for this endpoint
+ *
+ * "Path parts" are non-query-string URL segments, like "some" "path" in the URL
+ * `mydomain.com/some/path?and=a&query=string&too`. Because a well-formed path
+ * is necessary to execute a successful API request, we throw an error if the
+ * user has omitted a value (such as `/some/[missing component]/url`) or has
+ * provided a path part value that does not match the regular expression the
+ * API uses to goven that segment.
+ *
+ * @method
+ * @chainable
+ * @returns {WPRequest} The WPRequest instance (for chaining), if no errors were found
+ */
+WPRequest.prototype.validatePath = function() {
+	// Iterate through all _specified_ levels of this endpoint
+	var specifiedLevels = Object.keys( this._path )
+		.map(function( level ) {
+			return parseInt( level, 10 );
+		})
+		.filter(function( pathPartKey ) {
+			return ! isNaN( pathPartKey );
+		});
+
+	var maxLevel = Math.max.apply( null, specifiedLevels );
+
+	// Ensure that all necessary levels are specified
+	var path = [];
+	var valid = true;
+
+	for ( var level = 0; level <= maxLevel; level++ ) {
+
+		if ( ! this._levels || ! this._levels[ level ] ) {
+			continue;
+		}
+
+		if ( this._path[ level ] ) {
+			// Validate the provided path level against all available path validators
+			validatePathLevel( this._levels[ level ], this._path[ level ] );
+
+			// Add the path value to the array
+			path.push( this._path[ level ] );
+		} else {
+			path.push( ' ??? ' );
+			valid = false;
+		}
+	}
+
+	if ( ! valid ) {
+		throw new Error( 'Incomplete URL! Missing component: /' + path.join( '/' ) );
+	}
+
+	return this;
+};
+
+/**
+ * Set a parameter to render into the final query URI.
+ *
+ * @method
+ * @chainable
+ * @param {String|Object} props The name of the parameter to set, or an object containing
+ *                              parameter keys and their corresponding values
+ * @param {String|Number|Array} [value] The value of the parameter being set
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.param = function( props, value ) {
+	if ( ! props || typeof props === 'string' && value === undefined ) {
+		// We have no property to set, or no value to set for that property
+		return this;
+	}
+
+	// We can use the same iterator function below to handle explicit key-value
+	// pairs if we convert them into to an object we can iterate over:
+	if ( typeof props === 'string' ) {
+		props = keyValToObj( props, value );
+	}
+
+	// Iterate through the properties
+	Object.keys( props ).forEach(function( key ) {
+		var value = props[ key ];
+
+		// Arrays should be de-duped and sorted
+		if ( Array.isArray( value ) ) {
+			value = _unique( value ).sort( alphaNumericSort );
+		}
+
+		// Set the value
+		this._params[ key ] = value;
+	}.bind( this ));
+
+	return this;
+};
+
+// Globally-applicable parameters that impact the shape of the request or response
+// ===============================================================================
+
+/**
+ * Set the context of the request. Used primarily to expose private values on a
+ * request object by setting the context to "edit".
+ *
+ * @method
+ * @chainable
+ * @param {String} context The context to set on the request
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.context = paramSetter( 'context' );
+
+/**
+ * Convenience wrapper for `.context( 'edit' )`
+ *
+ * @method
+ * @chainable
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.edit = function() {
+	return this.context( 'edit' );
+};
+
+/**
+ * Return embedded resources as part of the response payload.
+ *
+ * @method
+ * @chainable
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.embed = function() {
+	return this.param( '_embed', true );
+};
+
+// Parameters supported by all/nearly all default collections
+// ==========================================================
+
+/**
+ * Set the pagination of a request. Use in conjunction with `.perPage()` for explicit
+ * pagination handling. (The number of pages in a response can be retrieved from the
+ * response's `_paging.totalPages` property.)
+ *
+ * @method
+ * @chainable
+ * @param {Number} pageNumber The page number of results to retrieve
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.page = paramSetter( 'page' );
+
+/**
+ * Set the number of items to be returned in a page of responses.
+ *
+ * @method
+ * @chainable
+ * @param {Number} itemsPerPage The number of items to return in one page of results
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.perPage = paramSetter( 'per_page' );
+
+/**
+ * Set an arbitrary offset to retrieve items from a specific point in a collection.
+ *
+ * @method
+ * @chainable
+ * @param {Number} offsetNumber The number of items by which to offset the response
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.offset = paramSetter( 'offset' );
+
+/**
+ * Change the sort direction of a returned collection
+ *
+ * @example <caption>order comments chronologically (oldest first)</caption>
+ *
+ *     site.comments().order( 'asc' )...
+ *
+ * @method
+ * @chainable
+ * @param {String} direction The order to use when sorting the response
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.order = paramSetter( 'order' );
+
+/**
+ * Order a collection by a specific field
+ *
+ * @method
+ * @chainable
+ * @param {String} field The field by which to order the response
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.orderby = paramSetter( 'orderby' );
+
+/**
+ * Filter results to those matching the specified search terms.
+ *
+ * @method
+ * @chainable
+ * @param {String} searchString A string to search for within post content
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.search = paramSetter( 'search' );
+
+/**
+ * Include specific resource IDs in the response collection.
+ *
+ * @method
+ * @chainable
+ * @param {Number|Number[]} ids An ID or array of IDs to include
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.include = paramSetter( 'include' );
+
+/**
+ * Exclude specific resource IDs in the response collection.
+ *
+ * @method
+ * @chainable
+ * @param {Number|Number[]} ids An ID or array of IDs to exclude
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.exclude = paramSetter( 'exclude' );
+
+/**
+ * Query a collection for members with a specific slug.
+ *
+ * @method
+ * @chainable
+ * @param {String} slug A post slug (slug), e.g. "hello-world"
+ * @returns The request instance (for chaining)
+ */
+WPRequest.prototype.slug = paramSetter( 'slug' );
+
+// HTTP Transport Prototype Methods
+// ================================
+
+// Chaining methods
+// ================
+
+/**
+ * Set the namespace of the request, e.g. to specify the API root for routes
+ * registered by wp core v2 ("wp/v2") or by any given plugin. Any previously-
+ * set namespace will be overwritten by subsequent calls to the method.
+ *
+ * @method
+ * @chainable
+ * @param {String} namespace A namespace string, e.g. "wp/v2"
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.namespace = function( namespace ) {
+	this._namespace = namespace;
+	return this;
+};
+
+/**
+ * Set a request to use authentication, and optionally provide auth credentials
+ *
+ * If auth credentials were already specified when the WPAPI instance was created, calling
+ * `.auth` on the request chain will set that request to use the existing credentials:
+ *
+ * @example <caption>use existing credentials</caption>
+ *
+ *     request.auth().get...
+ *
+ * Alternatively, a username & password (or nonce) can be explicitly passed into `.auth`:
+ *
+ * @example <caption>use explicit basic authentication credentials</caption>
+ *
+ *     request.auth({
+ *       username: 'admin',
+ *       password: 'super secure'
+ *     }).get...
+ *
+ * @example <caption>use a nonce for cookie authentication</caption>
+ *
+ *     request.auth({
+ *       nonce: 'somenonce'
+ *     })...
+ *
+ * @method
+ * @chainable
+ * @param {Object} credentials            An object with 'username' and 'password' string
+ *                                        properties, or else a 'nonce' property
+ * @param {String} [credentials.username] A WP-API Basic HTTP Authentication username
+ * @param {String} [credentials.password] A WP-API Basic HTTP Authentication password
+ * @param {String} [credentials.nonce]    A WP nonce for use with cookie authentication
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.auth = function( credentials ) {
+	if ( typeof credentials === 'object' ) {
+		if ( typeof credentials.username === 'string' ) {
+			this._options.username = credentials.username;
+		}
+
+		if ( typeof credentials.password === 'string' ) {
+			this._options.password = credentials.password;
+		}
+
+		if ( credentials.nonce ) {
+			this._options.nonce = credentials.nonce;
+		}
+	}
+
+	// Set the "auth" options flag that will force authentication on this request
+	this._options.auth = true;
+
+	return this;
+};
+
+/**
+ * Specify a file or a file buffer to attach to the request, for use when
+ * creating a new Media item
+ *
+ * @example <caption>within a server context</caption>
+ *
+ *     wp.media()
+ *       // Pass .file() the file system path to a file to upload
+ *       .file( '/path/to/file.jpg' )
+ *       .create({})...
+ *
+ * @example <caption>within a browser context</caption>
+ *
+ *     wp.media()
+ *       // Pass .file() the file reference from an HTML file input
+ *       .file( document.querySelector( 'input[type="file"]' ).files[0] )
+ *       .create({})...
+ *
+ * @method
+ * @chainable
+ * @param {string|object} file   A path to a file (in Node) or an file object
+ *                               (Node or Browser) to attach to the request
+ * @param {string}        [name] An (optional) filename to use for the file
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.file = function( file, name ) {
+	this._attachment = file;
+	// Explicitly set to undefined if not provided, to override any previously-
+	// set attachment name property that might exist from a prior `.file()` call
+	this._attachmentName = name ? name : undefined;
+	return this;
+};
+
+// HTTP Methods: Public Interface
+// ==============================
+
+/**
+ * Specify one or more headers to send with the dispatched HTTP request.
+ *
+ * @example <caption>Set a single header to be used on this request</caption>
+ *
+ *     request.setHeaders( 'Authorization', 'Bearer trustme' )...
+ *
+ * @example <caption>Set multiple headers to be used by this request</caption>
+ *
+ *     request.setHeaders({
+ *       Authorization: 'Bearer comeonwereoldfriendsright',
+ *       'Accept-Language': 'en-CA'
+ *     })...
+ *
+ * @since 1.1.0
+ * @method
+ * @chainable
+ * @param {String|Object} headers The name of the header to set, or an object of
+ *                                header names and their associated string values
+ * @param {String}        [value] The value of the header being set
+ * @returns {WPRequest} The WPRequest instance (for chaining)
+ */
+WPRequest.prototype.setHeaders = function( headers, value ) {
+	// We can use the same iterator function below to handle explicit key-value
+	// pairs if we convert them into to an object we can iterate over:
+	if ( typeof headers === 'string' ) {
+		headers = keyValToObj( headers, value );
+	}
+
+	this._options.headers = Object.assign( {}, this._options.headers || {}, headers );
+
+	return this;
+};
+
+/**
+ * Get (download the data for) the specified resource
+ *
+ * @method
+ * @async
+ * @param {Function} [callback] A callback to invoke with the results of the GET request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+WPRequest.prototype.get = function( callback ) {
+	return this.transport.get( this, callback );
+};
+
+/**
+ * Get the headers for the specified resource
+ *
+ * @method
+ * @async
+ * @param {Function} [callback] A callback to invoke with the results of the HEAD request
+ * @returns {Promise} A promise to the header results of the HTTP request
+ */
+WPRequest.prototype.headers = function( callback ) {
+	return this.transport.head( this, callback );
+};
+
+/**
+ * Create the specified resource with the provided data
+ *
+ * This is the public interface for creating POST requests
+ *
+ * @method
+ * @async
+ * @param {Object} data The data for the POST request
+ * @param {Function} [callback] A callback to invoke with the results of the POST request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+WPRequest.prototype.create = function( data, callback ) {
+	return this.transport.post( this, data, callback );
+};
+
+/**
+ * Update the specified resource with the provided data
+ *
+ * This is the public interface for creating PUT requests
+ *
+ * @method
+ * @async
+ * @private
+ * @param {Object} data The data for the PUT request
+ * @param {Function} [callback] A callback to invoke with the results of the PUT request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+WPRequest.prototype.update = function( data, callback ) {
+	return this.transport.put( this, data, callback );
+};
+
+/**
+ * Delete the specified resource
+ *
+ * @method
+ * @async
+ * @param {Object} [data] Data to send along with the DELETE request
+ * @param {Function} [callback] A callback to invoke with the results of the DELETE request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+WPRequest.prototype.delete = function( data, callback ) {
+	return this.transport.delete( this, data, callback );
+};
+
+/**
+ * Calling .then on a query chain will invoke the query as a GET and return a promise
+ *
+ * @method
+ * @async
+ * @param {Function} [successCallback] A callback to handle the data returned from the GET request
+ * @param {Function} [failureCallback] A callback to handle any errors encountered by the request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+WPRequest.prototype.then = function( successCallback, failureCallback ) {
+	return this.transport.get( this ).then( successCallback, failureCallback );
+};
+
+module.exports = WPRequest;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_endpoint-factories.js.html b/api-reference/wpapi/1.1.2/lib_endpoint-factories.js.html new file mode 100644 index 00000000..4a96a901 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_endpoint-factories.js.html @@ -0,0 +1,119 @@ + + + + + + lib/endpoint-factories.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/endpoint-factories.js

+ + + + + + + +
+
+
/**
+ * Take a WP route string (with PCRE named capture groups), `such as /author/(?P<id>\d+)`,
+ * and generate request handler factory methods for each represented endpoint.
+ *
+ * @module endpoint-factories
+ */
+'use strict';
+
+var extend = require( 'node.extend' );
+var createResourceHandlerSpec = require( './resource-handler-spec' ).create;
+var createEndpointRequest = require( './endpoint-request' ).create;
+var objectReduce = require( './util/object-reduce' );
+
+/**
+ * Given an array of route definitions and a specific namespace for those routes,
+ * recurse through the node tree representing all possible routes within the
+ * provided namespace to define path value setters (and corresponding property
+ * validators) for all possible variants of each resource's API endpoints.
+ *
+ * @method generate
+ * @param {string} namespace         The namespace string for these routes
+ * @param {object} routesByNamespace A dictionary of namespace - route definition
+ *                                   object pairs as generated from buildRouteTree,
+ *                                   where each route definition object is a dictionary
+ *                                   keyed by route definition strings
+ * @returns {object} A dictionary of endpoint request handler factories
+ */
+function generateEndpointFactories( routesByNamespace ) {
+
+	return objectReduce( routesByNamespace, function( namespaces, routeDefinitions, namespace ) {
+
+		// Create
+		namespaces[ namespace ] = objectReduce( routeDefinitions, function( handlers, routeDef, resource ) {
+
+			var handlerSpec = createResourceHandlerSpec( routeDef, resource );
+
+			var EndpointRequest = createEndpointRequest( handlerSpec, resource, namespace );
+
+			// "handler" object is now fully prepared; create the factory method that
+			// will instantiate and return a handler instance
+			handlers[ resource ] = function( options ) {
+				return new EndpointRequest( extend( {}, this._options, options ) );
+			};
+
+			// Expose the constructor as a property on the factory function, so that
+			// auto-generated endpoint request constructors may be further customized
+			// when needed
+			handlers[ resource ].Ctor = EndpointRequest;
+
+			return handlers;
+		}, {} );
+
+		return namespaces;
+	}, {} );
+}
+
+module.exports = {
+	generate: generateEndpointFactories
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_endpoint-request.js.html b/api-reference/wpapi/1.1.2/lib_endpoint-request.js.html new file mode 100644 index 00000000..17979a36 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_endpoint-request.js.html @@ -0,0 +1,133 @@ + + + + + + lib/endpoint-request.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/endpoint-request.js

+ + + + + + + +
+
+
/**
+ * @module endpoint-request
+ */
+'use strict';
+
+var inherit = require( 'util' ).inherits;
+var WPRequest = require( './constructors/wp-request' );
+var mixins = require( './mixins' );
+
+var applyMixin = require( './util/apply-mixin' );
+
+/**
+ * Create an endpoint request handler constructor for a specific resource tree
+ *
+ * @method create
+ * @param {Object} handlerSpec A resource handler specification object
+ * @param {String} resource    The root resource of requests created from the returned factory
+ * @param {String} namespace   The namespace string for the returned factory's handlers
+ * @returns {Function} A constructor inheriting from {@link WPRequest}
+ */
+function createEndpointRequest( handlerSpec, resource, namespace ) {
+
+	// Create the constructor function for this endpoint
+	function EndpointRequest( options ) {
+		WPRequest.call( this, options );
+
+		/**
+		 * Semi-private instance property specifying the available URL path options
+		 * for this endpoint request handler, keyed by ascending whole numbers.
+		 *
+		 * @property _levels
+		 * @type {object}
+		 * @private
+		 */
+		this._levels = handlerSpec._levels;
+
+		// Configure handler for this endpoint's root URL path & set namespace
+		this
+			.setPathPart( 0, resource )
+			.namespace( namespace );
+	}
+
+	inherit( EndpointRequest, WPRequest );
+
+	// Mix in all available shortcut methods for GET request query parameters that
+	// are valid within this endpoint tree
+	if ( typeof handlerSpec._getArgs === 'object' ) {
+		Object.keys( handlerSpec._getArgs ).forEach(function( supportedQueryParam ) {
+			var mixinsForParam = mixins[ supportedQueryParam ];
+
+			// Only proceed if there is a mixin available AND the specified mixins will
+			// not overwrite any previously-set prototype method
+			if ( typeof mixinsForParam === 'object' ) {
+				Object.keys( mixinsForParam ).forEach(function( methodName ) {
+					applyMixin( EndpointRequest.prototype, methodName, mixinsForParam[ methodName ] );
+				});
+			}
+		});
+	}
+
+	Object.keys( handlerSpec._setters ).forEach(function( setterFnName ) {
+		// Only assign setter functions if they do not overwrite preexisting methods
+		if ( ! EndpointRequest.prototype[ setterFnName ] ) {
+			EndpointRequest.prototype[ setterFnName ] = handlerSpec._setters[ setterFnName ];
+		}
+	});
+
+	return EndpointRequest;
+}
+
+module.exports = {
+	create: createEndpointRequest
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_http-transport.js.html b/api-reference/wpapi/1.1.2/lib_http-transport.js.html new file mode 100644 index 00000000..eb432b23 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_http-transport.js.html @@ -0,0 +1,450 @@ + + + + + + lib/http-transport.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/http-transport.js

+ + + + + + + +
+
+
/**
+ * @module http-transport
+ */
+'use strict';
+
+/*jshint -W079 */// Suppress warning about redefiniton of `Promise`
+var Promise = require( 'es6-promise' ).Promise;
+
+var agent = require( 'superagent' );
+var parseLinkHeader = require( 'li' ).parse;
+var url = require( 'url' );
+
+var WPRequest = require( './constructors/wp-request' );
+var checkMethodSupport = require( './util/check-method-support' );
+var extend = require( 'node.extend' );
+var objectReduce = require( './util/object-reduce' );
+var isEmptyObject = require( './util/is-empty-object' );
+
+/**
+ * Set any provided headers on the outgoing request object. Runs after _auth.
+ *
+ * @method _setHeaders
+ * @private
+ * @param {Object} request A superagent request object
+ * @param {Object} options A WPRequest _options object
+ * @param {Object} A superagent request object, with any available headers set
+ */
+function _setHeaders( request, options ) {
+	// If there's no headers, do nothing
+	if ( ! options.headers ) {
+		return request;
+	}
+
+	return objectReduce( options.headers, function( request, value, key ) {
+		return request.set( key, value );
+	}, request );
+}
+
+/**
+ * Conditionally set basic authentication on a server request object.
+ *
+ * @method _auth
+ * @private
+ * @param {Object} request A superagent request object
+ * @param {Object} options A WPRequest _options object
+ * @param {Boolean} forceAuthentication whether to force authentication on the request
+ * @param {Object} A superagent request object, conditionally configured to use basic auth
+ */
+function _auth( request, options, forceAuthentication ) {
+	// If we're not supposed to authenticate, don't even start
+	if ( ! forceAuthentication && ! options.auth && ! options.nonce ) {
+		return request;
+	}
+
+	// Enable nonce in options for Cookie authentication http://wp-api.org/guides/authentication.html
+	if ( options.nonce ) {
+		request.set( 'X-WP-Nonce', options.nonce );
+		return request;
+	}
+
+	// Retrieve the username & password from the request options if they weren't provided
+	var username = username || options.username;
+	var password = password || options.password;
+
+	// If no username or no password, can't authenticate
+	if ( ! username || ! password ) {
+		return request;
+	}
+
+	// Can authenticate: set basic auth parameters on the request
+	return request.auth( username, password );
+}
+
+// Pagination-Related Helpers
+// ==========================
+
+/**
+ * Combine the API endpoint root URI and link URI into a valid request URL.
+ * Endpoints are generally a full path to the JSON API's root endpoint, such
+ * as `website.com/wp-json`: the link headers, however, are returned as root-
+ * relative paths. Concatenating these would generate a URL such as
+ * `website.com/wp-json/wp-json/posts?page=2`: we must intelligently merge the
+ * URI strings in order to generate a valid new request URL.
+ *
+ * @private
+ * @param endpoint {String} The endpoint URL for the REST API root
+ * @param linkPath {String} A root-relative link path to an API request
+ * @returns {String} The full URL path to the provided link
+ */
+function mergeUrl( endpoint, linkPath ) {
+	var request = url.parse( endpoint );
+	linkPath = url.parse( linkPath, true );
+
+	// Overwrite relevant request URL object properties with the link's values:
+	// Setting these three values from the link will ensure proper URL generation
+	request.query = linkPath.query;
+	request.search = linkPath.search;
+	request.pathname = linkPath.pathname;
+
+	// Reassemble and return the merged URL
+	return url.format( request );
+}
+
+/**
+ * Extract the body property from the superagent response, or else try to parse
+ * the response text to get a JSON object.
+ *
+ * @private
+ * @param {Object} response      The response object from the HTTP request
+ * @param {String} response.text The response content as text
+ * @param {Object} response.body The response content as a JS object
+ * @returns {Object} The response content as a JS object
+ */
+function extractResponseBody( response ) {
+	var responseBody = response.body;
+	if ( isEmptyObject( responseBody ) && response.type === 'text/html' ) {
+		// Response may have come back as HTML due to caching plugin; try to parse
+		// the response text into JSON
+		try {
+			responseBody = JSON.parse( response.text );
+		} catch ( e ) {
+			// Swallow errors, it's OK to fall back to returning the body
+		}
+	}
+	return responseBody;
+}
+
+/**
+ * If the response is not paged, return the body as-is. If pagination
+ * information is present in the response headers, parse those headers into
+ * a custom `_paging` property on the response body. `_paging` contains links
+ * to the previous and next pages in the collection, as well as metadata
+ * about the size and number of pages in the collection.
+ *
+ * The structure of the `_paging` property is as follows:
+ *
+ * - `total` {Integer} The total number of records in the collection
+ * - `totalPages` {Integer} The number of pages available
+ * - `links` {Object} The parsed "links" headers, separated into individual URI strings
+ * - `next` {WPRequest} A WPRequest object bound to the "next" page (if page exists)
+ * - `prev` {WPRequest} A WPRequest object bound to the "previous" page (if page exists)
+ *
+ * @private
+ * @param {Object} result           The response object from the HTTP request
+ * @param {Object} options          The options hash from the original request
+ * @param {String} options.endpoint The base URL of the requested API endpoint
+ * @param {Object} httpTransport    The HTTP transport object used by the original request
+ * @returns {Object} The pagination metadata object for this HTTP request, or else null
+ */
+function createPaginationObject( result, options, httpTransport ) {
+	var _paging = null;
+
+	if ( ! result.headers || ! result.headers[ 'x-wp-totalpages' ] ) {
+		// No headers: return as-is
+		return _paging;
+	}
+
+	var totalPages = result.headers[ 'x-wp-totalpages' ];
+
+	if ( ! totalPages || totalPages === '0' ) {
+		// No paging: return as-is
+		return _paging;
+	}
+
+	// Decode the link header object
+	var links = result.headers.link ? parseLinkHeader( result.headers.link ) : {};
+
+	// Store pagination data from response headers on the response collection
+	_paging = {
+		total: result.headers[ 'x-wp-total' ],
+		totalPages: totalPages,
+		links: links
+	};
+
+	// Re-use any options from the original request, updating only the endpoint
+	// (this ensures that request properties like authentication are preserved)
+	var endpoint = options.endpoint;
+
+	// Create a WPRequest instance pre-bound to the "next" page, if available
+	if ( links.next ) {
+		_paging.next = new WPRequest( extend( {}, options, {
+			transport: httpTransport,
+			endpoint: mergeUrl( endpoint, links.next )
+		}));
+	}
+
+	// Create a WPRequest instance pre-bound to the "prev" page, if available
+	if ( links.prev ) {
+		_paging.prev = new WPRequest( extend( {}, options, {
+			transport: httpTransport,
+			endpoint: mergeUrl( endpoint, links.prev )
+		}));
+	}
+
+	return _paging;
+}
+
+// HTTP-Related Helpers
+// ====================
+
+/**
+ * Submit the provided superagent request object, invoke a callback (if it was
+ * provided), and return a promise to the response from the HTTP request.
+ *
+ * @private
+ * @param {Object} request A superagent request object
+ * @param {Function} callback A callback function (optional)
+ * @param {Function} transform A function to transform the result data
+ * @returns {Promise} A promise to the superagent request
+ */
+function invokeAndPromisify( request, callback, transform ) {
+	return new Promise(function( resolve, reject ) {
+		// Fire off the result
+		request.end(function( err, result ) {
+
+			// Return the results as a promise
+			if ( err || result.error ) {
+				reject( err || result.error );
+			} else {
+				resolve( result );
+			}
+		});
+	}).then( transform ).then(function( result ) {
+		// If a node-style callback was provided, call it, but also return the
+		// result value for use via the returned Promise
+		if ( callback && typeof callback === 'function' ) {
+			callback( null, result );
+		}
+		return result;
+	}, function( err ) {
+		// If the API provided an error object, it will be available within the
+		// superagent response object as response.body (containing the response
+		// JSON). If that object exists, it will have a .code property if it is
+		// truly an API error (non-API errors will not have a .code).
+		if ( err.response && err.response.body && err.response.body.code ) {
+			// Forward API error response JSON on to the calling method: omit
+			// all transport-specific (superagent-specific) properties
+			err = err.response.body;
+		}
+		// If a callback was provided, ensure it is called with the error; otherwise
+		// re-throw the error so that it can be handled by a Promise .catch or .then
+		if ( callback && typeof callback === 'function' ) {
+			callback( err );
+		} else {
+			throw err;
+		}
+	});
+}
+
+/**
+ * Return the body of the request, augmented with pagination information if the
+ * result is a paged collection.
+ *
+ * @private
+ * @param {WPRequest} wpreq The WPRequest representing the returned HTTP response
+ * @param {Object} result The results from the HTTP request
+ * @returns {Object} The "body" property of the result, conditionally augmented with
+ *                  pagination information if the result is a partial collection.
+ */
+function returnBody( wpreq, result ) {
+	var body = extractResponseBody( result );
+	var _paging = createPaginationObject( result, wpreq._options, wpreq.transport );
+	if ( _paging ) {
+		body._paging = _paging;
+	}
+	return body;
+}
+
+/**
+ * Extract and return the headers property from a superagent response object
+ *
+ * @private
+ * @param {Object} result The results from the HTTP request
+ * @returns {Object} The "headers" property of the result
+ */
+function returnHeaders( result ) {
+	return result.headers;
+}
+
+// HTTP Methods: Private HTTP-verb versions
+// ========================================
+
+/**
+ * @method get
+ * @async
+ * @param {WPRequest} wpreq A WPRequest query object
+ * @param {Function} [callback] A callback to invoke with the results of the GET request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+function _httpGet( wpreq, callback ) {
+	checkMethodSupport( 'get', wpreq );
+	var url = wpreq.toString();
+
+	var request = _auth( agent.get( url ), wpreq._options );
+	request = _setHeaders( request, wpreq._options );
+
+	return invokeAndPromisify( request, callback, returnBody.bind( null, wpreq ) );
+}
+
+/**
+ * Invoke an HTTP "POST" request against the provided endpoint
+ * @method post
+ * @async
+ * @param {WPRequest} wpreq A WPRequest query object
+ * @param {Object} data The data for the POST request
+ * @param {Function} [callback] A callback to invoke with the results of the POST request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+function _httpPost( wpreq, data, callback ) {
+	checkMethodSupport( 'post', wpreq );
+	var url = wpreq.toString();
+	data = data || {};
+	var request = _auth( agent.post( url ), wpreq._options, true );
+	request = _setHeaders( request, wpreq._options );
+
+	if ( wpreq._attachment ) {
+		// Data must be form-encoded alongside image attachment
+		request = objectReduce( data, function( req, value, key ) {
+			return req.field( key, value );
+		}, request.attach( 'file', wpreq._attachment, wpreq._attachmentName ) );
+	} else {
+		request = request.send( data );
+	}
+
+	return invokeAndPromisify( request, callback, returnBody.bind( null, wpreq ) );
+}
+
+/**
+ * @method put
+ * @async
+ * @param {WPRequest} wpreq A WPRequest query object
+ * @param {Object} data The data for the PUT request
+ * @param {Function} [callback] A callback to invoke with the results of the PUT request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+function _httpPut( wpreq, data, callback ) {
+	checkMethodSupport( 'put', wpreq );
+	var url = wpreq.toString();
+	data = data || {};
+
+	var request = _auth( agent.put( url ), wpreq._options, true ).send( data );
+	request = _setHeaders( request, wpreq._options );
+
+	return invokeAndPromisify( request, callback, returnBody.bind( null, wpreq ) );
+}
+
+/**
+ * @method delete
+ * @async
+ * @param {WPRequest} wpreq A WPRequest query object
+ * @param {Object} [data] Data to send along with the DELETE request
+ * @param {Function} [callback] A callback to invoke with the results of the DELETE request
+ * @returns {Promise} A promise to the results of the HTTP request
+ */
+function _httpDelete( wpreq, data, callback ) {
+	if ( ! callback && typeof data === 'function' ) {
+		callback = data;
+		data = null;
+	}
+	checkMethodSupport( 'delete', wpreq );
+	var url = wpreq.toString();
+	var request = _auth( agent.del( url ), wpreq._options, true ).send( data );
+	request = _setHeaders( request, wpreq._options );
+
+	return invokeAndPromisify( request, callback, returnBody.bind( null, wpreq ) );
+}
+
+/**
+ * @method head
+ * @async
+ * @param {WPRequest} wpreq A WPRequest query object
+ * @param {Function} [callback] A callback to invoke with the results of the HEAD request
+ * @returns {Promise} A promise to the header results of the HTTP request
+ */
+function _httpHead( wpreq, callback ) {
+	checkMethodSupport( 'head', wpreq );
+	var url = wpreq.toString();
+	var request = _auth( agent.head( url ), wpreq._options );
+	request = _setHeaders( request, wpreq._options );
+
+	return invokeAndPromisify( request, callback, returnHeaders );
+}
+
+module.exports = {
+	delete: _httpDelete,
+	get: _httpGet,
+	head: _httpHead,
+	post: _httpPost,
+	put: _httpPut
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_mixins_filters.js.html b/api-reference/wpapi/1.1.2/lib_mixins_filters.js.html new file mode 100644 index 00000000..7a58eeed --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_mixins_filters.js.html @@ -0,0 +1,258 @@ + + + + + + lib/mixins/filters.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/mixins/filters.js

+ + + + + + + +
+
+
/**
+ * @module mixins/filters
+ */
+'use strict';
+
+var _unique = require( 'lodash.uniq' );
+var extend = require( 'node.extend' );
+
+var alphaNumericSort = require( '../util/alphanumeric-sort' );
+var keyValToObj = require( '../util/key-val-to-obj' );
+
+/**
+ * Filter methods that can be mixed in to a request constructor's prototype to
+ * allow that request to take advantage of the `?filter[]=` aliases for WP_Query
+ * parameters for collection endpoints, when available.
+ *
+ * @mixin filters
+ */
+var filterMixins = {};
+
+// Filter Methods
+// ==============
+
+/**
+ * Specify key-value pairs by which to filter the API results (commonly used
+ * to retrieve only posts meeting certain criteria, such as posts within a
+ * particular category or by a particular author).
+ *
+ * @example
+ *
+ *     // Set a single property:
+ *     wp.filter( 'post_type', 'cpt_event' )...
+ *
+ *     // Set multiple properties at once:
+ *     wp.filter({
+ *         post_status: 'publish',
+ *         category_name: 'news'
+ *     })...
+ *
+ *     // Chain calls to .filter():
+ *     wp.filter( 'post_status', 'publish' ).filter( 'category_name', 'news' )...
+ *
+ * @method filter
+ * @chainable
+ * @param {String|Object} props A filter property name string, or object of name/value pairs
+ * @param {String|Number|Array} [value] The value(s) corresponding to the provided filter property
+ * @returns The request instance (for chaining)
+ */
+filterMixins.filter = function( props, value ) {
+	/* jshint validthis:true */
+
+	if ( ! props || typeof props === 'string' && value === undefined ) {
+		// We have no filter to set, or no value to set for that filter
+		return this;
+	}
+
+	// convert the property name string `props` and value `value` into an object
+	if ( typeof props === 'string' ) {
+		props = keyValToObj( props, value );
+	}
+
+	this._filters = extend( this._filters, props );
+
+	return this;
+};
+
+/**
+ * Restrict the query results to posts matching one or more taxonomy terms.
+ *
+ * @method taxonomy
+ * @chainable
+ * @param {String} taxonomy The name of the taxonomy to filter by
+ * @param {String|Number|Array} term A string or integer, or array thereof, representing terms
+ * @returns The request instance (for chaining)
+ */
+filterMixins.taxonomy = function( taxonomy, term ) {
+	/* jshint validthis:true */
+	var termIsArray = Array.isArray( term );
+	var termIsNumber = termIsArray ?
+		term.reduce(function( allAreNumbers, term ) {
+			return allAreNumbers && typeof term === 'number';
+		}, true ) :
+		typeof term === 'number';
+	var termIsString = termIsArray ?
+		term.reduce(function( allAreStrings, term ) {
+			return allAreStrings && typeof term === 'string';
+		}, true ) :
+		typeof term === 'string';
+	var taxonomyTerms;
+
+	if ( ! termIsString && ! termIsNumber ) {
+		throw new Error( 'term must be a number, string, or array of numbers or strings' );
+	}
+
+	if ( taxonomy === 'category' ) {
+		if ( termIsString ) {
+			// Query param for filtering by category slug is "category_name"
+			taxonomy = 'category_name';
+		} else {
+			// The boolean check above ensures that if taxonomy === 'category' and
+			// term is not a string, then term must be a number and therefore an ID:
+			// Query param for filtering by category ID is "cat"
+			taxonomy = 'cat';
+		}
+	} else if ( taxonomy === 'post_tag' ) {
+		// tag is used in place of post_tag in the public query variables
+		taxonomy = 'tag';
+	}
+
+	// Ensure the taxonomy filters object is available
+	this._taxonomyFilters = this._taxonomyFilters || {};
+
+	// Ensure there's an array of terms available for this taxonomy
+	taxonomyTerms = ( this._taxonomyFilters[ taxonomy ] || [] )
+		// Insert the provided terms into the specified taxonomy's terms array
+		.concat( term )
+		// Sort array
+		.sort( alphaNumericSort );
+
+	// De-dupe
+	this._taxonomyFilters[ taxonomy ] = _unique( taxonomyTerms, true );
+
+	return this;
+};
+
+/**
+ * Query for posts published in a given year.
+ *
+ * @method year
+ * @chainable
+ * @param {Number} year integer representation of year requested
+ * @returns The request instance (for chaining)
+ */
+filterMixins.year = function( year ) {
+	/* jshint validthis:true */
+	return filterMixins.filter.call( this, 'year', year );
+};
+
+/**
+ * Query for posts published in a given month, either by providing the number
+ * of the requested month (e.g. 3), or the month's name as a string (e.g. "March")
+ *
+ * @method month
+ * @chainable
+ * @param {Number|String} month Integer for month (1) or month string ("January")
+ * @returns The request instance (for chaining)
+ */
+filterMixins.month = function( month ) {
+	/* jshint validthis:true */
+	var monthDate;
+	if ( typeof month === 'string' ) {
+		// Append a arbitrary day and year to the month to parse the string into a Date
+		monthDate = new Date( Date.parse( month + ' 1, 2012' ) );
+
+		// If the generated Date is NaN, then the passed string is not a valid month
+		if ( isNaN( monthDate ) ) {
+			return this;
+		}
+
+		// JS Dates are 0 indexed, but the WP API requires a 1-indexed integer
+		month = monthDate.getMonth() + 1;
+	}
+
+	// If month is a Number, add the monthnum filter to the request
+	if ( typeof month === 'number' ) {
+		return filterMixins.filter.call( this, 'monthnum', month );
+	}
+
+	return this;
+};
+
+/**
+ * Add the day filter into the request to retrieve posts for a given day
+ *
+ * @method day
+ * @chainable
+ * @param {Number} day Integer representation of the day requested
+ * @returns The request instance (for chaining)
+ */
+filterMixins.day = function( day ) {
+	/* jshint validthis:true */
+	return filterMixins.filter.call( this, 'day', day );
+};
+
+/**
+ * Specify that we are requesting a page by its path (specific to Page resources)
+ *
+ * @method path
+ * @chainable
+ * @param {String} path The root-relative URL path for a page
+ * @returns The request instance (for chaining)
+ */
+filterMixins.path = function( path ) {
+	/* jshint validthis:true */
+	return filterMixins.filter.call( this, 'pagename', path );
+};
+
+module.exports = filterMixins;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_mixins_index.js.html b/api-reference/wpapi/1.1.2/lib_mixins_index.js.html new file mode 100644 index 00000000..94285d59 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_mixins_index.js.html @@ -0,0 +1,120 @@ + + + + + + lib/mixins/index.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/mixins/index.js

+ + + + + + + +
+
+
/**
+ * This module defines a mapping between supported GET request query parameter
+ * arguments and their corresponding mixin, if available.
+ */
+'use strict';
+
+var filterMixins = require( './filters' );
+var parameterMixins = require( './parameters' );
+
+// `.context`, `.embed`, and `.edit` (a shortcut for `context(edit, true)`) are
+// supported by default in WPRequest, as is the base `.param` method. Any GET
+// argument parameters not covered here must be set directly by using `.param`.
+
+// The initial mixins we define are the ones where either a single property
+// accepted by the API endpoint corresponds to multiple individual mixin
+// functions, or where the name we use for the function diverges from that
+// of the query parameter that the mixin sets.
+var mixins = {
+	categories: {
+		categories: parameterMixins.categories,
+		/** @deprecated use .categories() */
+		category: parameterMixins.category
+	},
+	categories_exclude: {
+		excludeCategories: parameterMixins.excludeCategories
+	},
+	tags: {
+		tags: parameterMixins.tags,
+		/** @deprecated use .tags() */
+		tag: parameterMixins.tag
+	},
+	tags_exclude: {
+		excludeTags: parameterMixins.excludeTags
+	},
+	filter: filterMixins,
+	post: {
+		post: parameterMixins.post,
+		/** @deprecated use .post() */
+		forPost: parameterMixins.post
+	}
+};
+
+// All of these parameter mixins use a setter function named identically to the
+// property that the function sets, but they must still be provided in wrapper
+// objects so that the mixin can be `.assign`ed correctly: wrap & assign each
+// setter to the mixins dictionary object.
+[
+	'after',
+	'author',
+	'before',
+	'parent',
+	'password',
+	'status',
+	'sticky'
+].forEach(function( mixinName ) {
+	mixins[ mixinName ] = {};
+	mixins[ mixinName ][ mixinName ] = parameterMixins[ mixinName ];
+});
+
+module.exports = mixins;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_mixins_parameters.js.html b/api-reference/wpapi/1.1.2/lib_mixins_parameters.js.html new file mode 100644 index 00000000..c3ffda55 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_mixins_parameters.js.html @@ -0,0 +1,318 @@ + + + + + + lib/mixins/parameters.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/mixins/parameters.js

+ + + + + + + +
+
+
/**
+ * Filter methods that can be mixed in to a request constructor's prototype to
+ * allow that request to take advantage of top-level query parameters for
+ * collection endpoints. These are most relevant to posts, pages and CPTs, but
+ * pagination helpers are applicable to any collection.
+ *
+ * @module mixins/parameters
+ */
+'use strict';
+/*jshint -W106 */// Disable underscore_case warnings in this file b/c WP uses them
+
+var paramSetter = require( '../util/parameter-setter' );
+var argumentIsNumeric = require( '../util/argument-is-numeric' );
+
+/**
+ * @mixin parameters
+ */
+var parameterMixins = {};
+
+var filters = require( './filters' );
+// Needed for .author mixin, as author by ID is a parameter and by Name is a filter
+var filter = filters.filter;
+// Needed for .tag and .category mixin, for deprecated query-by-slug support
+var taxonomy = filters.taxonomy;
+
+// Parameter Methods
+// =================
+
+/**
+ * Query for posts by a specific author.
+ * This method will replace any previous 'author' query parameters that had been set.
+ *
+ * Note that this method will either set the "author" top-level query parameter,
+ * or else the "author_name" filter parameter (when querying by nicename): this is
+ * irregular as most parameter helper methods either set a top level parameter or a
+ * filter, not both.
+ *
+ * _Usage with the author nicename string is deprecated._ Query by author ID instead.
+ * If the "rest-filter" plugin is not installed, the name query will have no effect.
+ *
+ * @method author
+ * @chainable
+ * @param {String|Number} author The nicename or ID for a particular author
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.author = function( author ) {
+	/* jshint validthis:true */
+	if ( author === undefined ) {
+		return this;
+	}
+	if ( typeof author === 'string' ) {
+		this.param( 'author', null );
+		return filter.call( this, 'author_name', author );
+	}
+	if ( typeof author === 'number' ) {
+		filter.call( this, 'author_name', null );
+		return this.param( 'author', author );
+	}
+	if ( author === null ) {
+		filter.call( this, 'author_name', null );
+		return this.param( 'author', null );
+	}
+	throw new Error( 'author must be either a nicename string or numeric ID' );
+};
+
+/**
+ * Search for hierarchical taxonomy terms that are children of the parent term
+ * indicated by the provided term ID
+ *
+ * @example
+ *
+ *     wp.pages().parent( 3 ).then(function( pages ) {
+ *       // console.log( 'all of these pages are nested below page ID#3:' );
+ *       // console.log( pages );
+ *     });
+ *
+ *     wp.categories().parent( 42 ).then(function( categories ) {
+ *       console.log( 'all of these categories are sub-items of cat ID#42:' );
+ *       console.log( categories );
+ *     });
+ *
+ * @method parent
+ * @chainable
+ * @param {Number} parentId The ID of a (hierarchical) taxonomy term
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.parent = paramSetter( 'parent' );
+
+/**
+ * Specify the post for which to retrieve terms (relevant for *e.g.* taxonomy
+ * and comment collection endpoints).
+ *
+ * @method post
+ * @chainable
+ * @param {String|Number} post The ID of the post for which to retrieve terms
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.post = paramSetter( 'post' );
+
+/**
+ * Specify the password to use to access the content of a password-protected post
+ *
+ * @method password
+ * @chainable
+ * @param {string} password A string password to access protected content within a post
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.password = paramSetter( 'password' );
+
+/**
+ * Specify for which post statuses to return posts in a response collection
+ *
+ * See https://codex.wordpress.org/Post_Status -- the default post status
+ * values in WordPress which are most relevant to the API are 'publish',
+ * 'future', 'draft', 'pending', 'private', and 'trash'. This parameter also
+ * supports passing the special value "any" to return all statuses.
+ *
+ * @method status
+ * @chainable
+ * @param {string|string[]} status A status name string or array of strings
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.status = paramSetter( 'status' );
+
+/**
+ * Specify whether to return only, or to completely exclude, sticky posts
+ *
+ * @method sticky
+ * @chainable
+ * @param {boolean} sticky A boolean value for whether ONLY sticky posts (true) or
+ *                         NO sticky posts (false) should be returned in the query
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.sticky = paramSetter( 'sticky' );
+
+// Taxonomy Term Filter Methods
+// ============================
+
+/**
+ * Retrieve only records associated with one of the provided categories
+ *
+ * @method categories
+ * @chainable
+ * @param {String|Number|Array} categories A term ID integer or numeric string, or array thereof
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.categories = paramSetter( 'categories' );
+
+/**
+ * Legacy wrapper for `.categories()` that uses `?filter` to query by slug if available
+ *
+ * @method tag
+ * @deprecated Use `.categories()` and query by category IDs
+ * @chainable
+ * @param {String|Number|Array} tag A category term slug string, numeric ID, or array of numeric IDs
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.category = function( category ) {
+	/* jshint validthis:true */
+	if ( argumentIsNumeric( category ) ) {
+		return parameterMixins.categories.call( this, category );
+	}
+	return taxonomy.call( this, 'category', category );
+};
+
+/**
+ * Exclude records associated with any of the provided category IDs
+ *
+ * @method excludeCategories
+ * @chainable
+ * @param {String|Number|Array} category A term ID integer or numeric string, or array thereof
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.excludeCategories = paramSetter( 'categories_exclude' );
+
+/**
+ * Retrieve only records associated with one of the provided tag IDs
+ *
+ * @method tags
+ * @chainable
+ * @param {String|Number|Array} tags A term ID integer or numeric string, or array thereof
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.tags = paramSetter( 'tags' );
+
+/**
+ * Legacy wrapper for `.tags()` that uses `?filter` to query by slug if available
+ *
+ * @method tag
+ * @deprecated Use `.tags()` and query by term IDs
+ * @chainable
+ * @param {String|Number|Array} tag A tag term slug string, numeric ID, or array of numeric IDs
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.tag = function( tag ) {
+	/* jshint validthis:true */
+	if ( argumentIsNumeric( tag ) ) {
+		return parameterMixins.tags.call( this, tag );
+	}
+	return taxonomy.call( this, 'tag', tag );
+};
+
+/**
+ * Exclude records associated with any of the provided tag IDs
+ *
+ * @method excludeTags
+ * @chainable
+ * @param {String|Number|Array} category A term ID integer or numeric string, or array thereof
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.excludeTags = paramSetter( 'tags_exclude' );
+
+// Date Methods
+// ============
+
+/**
+ * Retrieve only records published before a specified date
+ *
+ * @example <caption>Provide an ISO 8601-compliant date string</caption>
+ *
+ *     wp.posts().before('2016-03-22')...
+ *
+ * @example <caption>Provide a JavaScript Date object</caption>
+ *
+ *     wp.posts().before( new Date( 2016, 03, 22 ) )...
+ *
+ * @method before
+ * @chainable
+ * @param {String|Date} date An ISO 8601-compliant date string, or Date object
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.before = function( date ) {
+	/* jshint validthis:true */
+	return this.param( 'before', new Date( date ).toISOString() );
+};
+
+/**
+ * Retrieve only records published after a specified date
+ *
+ * @example <caption>Provide an ISO 8601-compliant date string</caption>
+ *
+ *     wp.posts().after('1986-03-22')...
+ *
+ * @example <caption>Provide a JavaScript Date object</caption>
+ *
+ *     wp.posts().after( new Date( 1986, 03, 22 ) )...
+ *
+ * @method after
+ * @chainable
+ * @param {String|Date} date An ISO 8601-compliant date string, or Date object
+ * @returns The request instance (for chaining)
+ */
+parameterMixins.after = function( date ) {
+	/* jshint validthis:true */
+	return this.param( 'after', new Date( date ).toISOString() );
+};
+
+module.exports = parameterMixins;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_path-part-setter.js.html b/api-reference/wpapi/1.1.2/lib_path-part-setter.js.html new file mode 100644 index 00000000..2f08c572 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_path-part-setter.js.html @@ -0,0 +1,151 @@ + + + + + + lib/path-part-setter.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/path-part-setter.js

+ + + + + + + +
+
+
/**
+ * @module path-part-setter
+ */
+'use strict';
+
+/**
+ * Return a function to set part of the request URL path.
+ *
+ * Path part setter methods may be either dynamic (*i.e.* may represent a
+ * "named group") or non-dynamic (representing a static part of the URL, which
+ * is usually a collection endpoint of some sort). Which type of function is
+ * returned depends on whether a given route has one or many sub-resources.
+ *
+ * @alias module:lib/path-part-setter.create
+ * @param {Object} node An object representing a level of an endpoint path hierarchy
+ * @returns {Function} A path part setter function
+ */
+function createPathPartSetter( node ) {
+	// Local references to `node` properties used by returned functions
+	var nodeLevel = node.level;
+	var nodeName = node.names[ 0 ];
+	var supportedMethods = node.methods || [];
+	var dynamicChildren = node.children ? Object.keys( node.children )
+		.map(function( key ) {
+			return node.children[ key ];
+		})
+		.filter(function( childNode ) {
+			return childNode.namedGroup === true;
+		}) : [];
+	var dynamicChild = dynamicChildren.length === 1 && dynamicChildren[ 0 ];
+	var dynamicChildLevel = dynamicChild && dynamicChild.level;
+
+	if ( node.namedGroup ) {
+		/**
+		 * Set a dymanic (named-group) path part of a query URL.
+		 *
+		 * @example
+		 *
+		 *     // id() is a dynamic path part setter:
+		 *     wp.posts().id( 7 ); // Get posts/7
+		 *
+		 * @chainable
+		 * @param  {String|Number} val The path part value to set
+		 * @returns {Object} The handler instance (for chaining)
+		 */
+		return function( val ) {
+			/* jshint validthis:true */
+			this.setPathPart( nodeLevel, val );
+			if ( supportedMethods.length ) {
+				this._supportedMethods = supportedMethods;
+			}
+			return this;
+		};
+	} else {
+		/**
+		 * Set a non-dymanic (non-named-group) path part of a query URL, and
+		 * set the value of a subresource if an input value is provided and
+		 * exactly one named-group child node exists.
+		 *
+		 * @example
+		 *
+		 *     // revisions() is a non-dynamic path part setter:
+		 *     wp.posts().id( 4 ).revisions();       // Get posts/4/revisions
+		 *     wp.posts().id( 4 ).revisions( 1372 ); // Get posts/4/revisions/1372
+		 *
+		 * @chainable
+		 * @param  {String|Number} [val] The path part value to set (if provided)
+		 *                               for a subresource within this resource
+		 * @returns {Object} The handler instance (for chaining)
+		 */
+		return function( val ) {
+			/* jshint validthis:true */
+			// If the path part is not a namedGroup, it should have exactly one
+			// entry in the names array: use that as the value for this setter,
+			// as it will usually correspond to a collection endpoint.
+			this.setPathPart( nodeLevel, nodeName );
+
+			// If this node has exactly one dynamic child, this method may act as
+			// a setter for that child node. `dynamicChildLevel` will be falsy if the
+			// node does not have a child or has multiple children.
+			if ( val !== undefined && dynamicChildLevel ) {
+				this.setPathPart( dynamicChildLevel, val );
+			}
+			return this;
+		};
+	}
+}
+
+module.exports = {
+	create: createPathPartSetter
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_resource-handler-spec.js.html b/api-reference/wpapi/1.1.2/lib_resource-handler-spec.js.html new file mode 100644 index 00000000..7912e3c2 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_resource-handler-spec.js.html @@ -0,0 +1,186 @@ + + + + + + lib/resource-handler-spec.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/resource-handler-spec.js

+ + + + + + + +
+
+
/**
+ * @module resource-handler-spec
+ */
+'use strict';
+
+var createPathPartSetter = require( './path-part-setter' ).create;
+
+/** @private */
+function addLevelOption( levelsObj, level, obj ) {
+	levelsObj[ level ] = levelsObj[ level ] || [];
+	levelsObj[ level ].push( obj );
+}
+
+/**
+ * Assign a setter function for the provided node to the provided route
+ * handler object setters dictionary (mutates handler by reference).
+ *
+ * @private
+ * @param {Object} handler A route handler definition object
+ * @param {Object} node    A route hierarchy level node object
+ */
+function assignSetterFnForNode( handler, node ) {
+	var setterFn;
+
+	// For each node, add its handler to the relevant "level" representation
+	addLevelOption( handler._levels, node.level, {
+		component: node.component,
+		validate: node.validate,
+		methods: node.methods
+	});
+
+	// First level is set implicitly, no dedicated setter needed
+	if ( node.level > 0 ) {
+
+		setterFn = createPathPartSetter( node );
+
+		node.names.forEach(function( name ) {
+			// Convert from snake_case to camelCase
+			var setterFnName = name
+				.replace( /[_-]+\w/g, function( match ) {
+					return match.replace( /[_-]+/, '' ).toUpperCase();
+				});
+
+			// Don't overwrite previously-set methods
+			if ( ! handler._setters[ setterFnName ] ) {
+				handler._setters[ setterFnName ] = setterFn;
+			}
+		});
+	}
+}
+
+/**
+ * Walk the tree of a specific resource node to create the setter methods
+ *
+ * The API we want to produce from the node tree looks like this:
+ *
+ *     wp.posts();                        /wp/v2/posts
+ *     wp.posts().id( 7 );                /wp/v2/posts/7
+ *     wp.posts().id( 7 ).revisions();    /wp/v2/posts/7/revisions
+ *     wp.posts().id( 7 ).revisions( 8 ); /wp/v2/posts/7/revisions/8
+ *
+ * ^ That last one's the tricky one: we can deduce that this parameter is "id", but
+ * that param will already be taken by the post ID, so sub-collections have to be
+ * set up as `.revisions()` to get the collection, and `.revisions( id )` to get a
+ * specific resource.
+ *
+ * @private
+ * @param  {Object} node            A node object
+ * @param  {Object} [node.children] An object of child nodes
+ * // @returns {isLeaf} A boolean indicating whether the processed node is a leaf
+ */
+function extractSetterFromNode( handler, node ) {
+
+	assignSetterFnForNode( handler, node );
+
+	if ( node.children ) {
+		// Recurse down to this node's children
+		Object.keys( node.children ).forEach(function( key ) {
+			extractSetterFromNode( handler, node.children[ key ] );
+		});
+	}
+}
+
+/**
+ * Create a node handler specification object from a route definition object
+ *
+ * @name create
+ * @param {object} routeDefinition A route definition object
+ * @param {string} resource The string key of the resource for which to create a handler
+ * @returns {object} A handler spec object with _path, _levels and _setters properties
+ */
+function createNodeHandlerSpec( routeDefinition, resource ) {
+
+	var handler = {
+		// A "path" is an ordered (by key) set of values composed into the final URL
+		_path: {
+			'0': resource
+		},
+
+		// A "level" is a level-keyed object representing the valid options for
+		// one level of the resource URL
+		_levels: {},
+
+		// Objects that hold methods and properties which will be copied to
+		// instances of this endpoint's handler
+		_setters: {},
+
+		// Arguments (query parameters) that may be set in GET requests to endpoints
+		// nested within this resource route tree, used to determine the mixins to
+		// add to the request handler
+		_getArgs: routeDefinition._getArgs
+	};
+
+	// Walk the tree
+	Object.keys( routeDefinition ).forEach(function( routeDefProp ) {
+		if ( routeDefProp !== '_getArgs' ) {
+			extractSetterFromNode( handler, routeDefinition[ routeDefProp ] );
+		}
+	});
+
+	return handler;
+}
+
+module.exports = {
+	create: createNodeHandlerSpec
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_route-tree.js.html b/api-reference/wpapi/1.1.2/lib_route-tree.js.html new file mode 100644 index 00000000..52fe73a0 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_route-tree.js.html @@ -0,0 +1,263 @@ + + + + + + lib/route-tree.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/route-tree.js

+ + + + + + + +
+
+
/**
+ * @module route-tree
+ */
+'use strict';
+
+var namedGroupRE = require( './util/named-group-regexp' ).namedGroupRE;
+var splitPath = require( './util/split-path' );
+var ensure = require( './util/ensure' );
+var objectReduce = require( './util/object-reduce' );
+
+/**
+ * Method to use when reducing route components array.
+ *
+ * @private
+ * @param {object} routeObj     A route definition object (set via .bind partial application)
+ * @param {object} topLevel     The top-level route tree object for this set of routes (set
+ *                              via .bind partial application)
+ * @param {object} parentLevel  The memo object, which is mutated as the reducer adds
+ *                              a new level handler for each level in the route
+ * @param {string} component    The string defining this route component
+ * @param {number} idx          The index of this component within the components array
+ * @param {string[]} components The array of all components
+ * @returns {object} The child object of the level being reduced
+ */
+function reduceRouteComponents( routeObj, topLevel, parentLevel, component, idx, components ) {
+	// Check to see if this component is a dynamic URL segment (i.e. defined by
+	// a named capture group regular expression). namedGroup will be `null` if
+	// the regexp does not match, or else an array defining the RegExp match, e.g.
+	// [
+	//   'P<id>[\\d]+)',
+	//   'id', // Name of the group
+	//   '[\\d]+', // regular expression for this URL segment's contents
+	//   index: 15,
+	//   input: '/wp/v2/posts/(?P<id>[\\d]+)'
+	// ]
+	var namedGroup = component.match( namedGroupRE );
+	// Pull out references to the relevant indices of the match, for utility:
+	// `null` checking is necessary in case the component did not match the RE,
+	// hence the `namedGroup &&`.
+	var groupName = namedGroup && namedGroup[ 1 ];
+	var groupPattern = namedGroup && namedGroup[ 2 ];
+
+	// When branching based on a dynamic capture group we used the group's RE
+	// pattern as the unique identifier: this is done because the same group
+	// could be assigned different names in different endpoint handlers, e.g.
+	// "id" for posts/:id vs "parent_id" for posts/:parent_id/revisions.
+	//
+	// There is an edge case where groupPattern will be "" if we are registering
+	// a custom route via `.registerRoute` that does not include parameter
+	// validation. In this case we assume the groupName is sufficiently unique,
+	// and fall back to `|| groupName` for the levelKey string.
+	var levelKey = namedGroup ? ( groupPattern || groupName ) : component;
+
+	// Level name on the other hand takes its value from the group's name, if
+	// defined, and falls back to the component string to handle situations where
+	// `component` is a collection (e.g. "revisions")
+	var levelName = namedGroup ? groupName : component;
+
+	// Check whether we have a preexisting node at this level of the tree, and
+	// create a new level object if not. The component string is included so that
+	// validators can throw meaningful errors as appropriate.
+	var currentLevel = parentLevel[ levelKey ] || {
+		component: component,
+		namedGroup: namedGroup ? true : false,
+		level: idx,
+		names: []
+	};
+
+	// A level's "names" correspond to the list of strings which could describe
+	// an endpoint's component setter functions: "id", "revisions", etc.
+	if ( currentLevel.names.indexOf( levelName ) < 0 ) {
+		currentLevel.names.push( levelName );
+	}
+
+	// A level's validate method is called to check whether a value being set
+	// on the request URL is of the proper type for the location in which it
+	// is specified. If a group pattern was found, the validator checks whether
+	// the input string exactly matches the group pattern.
+	var groupPatternRE = groupPattern === '' ?
+		// If groupPattern is an empty string, accept any input without validation
+		/.*/ :
+		// Otherwise, validate against the group pattern or the component string
+		new RegExp( groupPattern ? '^' + groupPattern + '$' : component, 'i' );
+
+	// Only one validate function is maintained for each node, because each node
+	// is defined either by a string literal or by a specific regular expression.
+	currentLevel.validate = function( input ) {
+		return groupPatternRE.test( input );
+	};
+
+	// Check to see whether to expect more nodes within this branch of the tree,
+	if ( components[ idx + 1 ] ) {
+		// and create a "children" object to hold those nodes if necessary
+		currentLevel.children = currentLevel.children || {};
+	} else {
+		// At leaf nodes, specify the method capabilities of this endpoint
+		currentLevel.methods = ( routeObj.methods || [] ).map(function( str ) {
+			return str.toLowerCase();
+		});
+		// Ensure HEAD is included whenever GET is supported: the API automatically
+		// adds support for HEAD if you have GET
+		if ( currentLevel.methods.indexOf( 'get' ) > -1 && currentLevel.methods.indexOf( 'head' ) === -1 ) {
+			currentLevel.methods.push( 'head' );
+		}
+
+		// At leaf nodes also flag (at the top level) what arguments are
+		// available to GET requests, so that we may automatically apply the
+		// appropriate parameter mixins
+		if ( routeObj.endpoints ) {
+			topLevel._getArgs = topLevel._getArgs || {};
+			routeObj.endpoints.forEach(function( endpoint ) {
+				// `endpoint.methods` will be an array of methods like `[ 'GET' ]`: we
+				// only care about GET for this exercise. Validating POST and PUT args
+				// could be useful but is currently deemed to be out-of-scope.
+				endpoint.methods.forEach(function( method ) {
+					if ( method.toLowerCase() === 'get' ) {
+						Object.keys( endpoint.args ).forEach(function( argKey ) {
+							// Reference param definition objects in the top _getArgs dictionary
+							topLevel._getArgs[ argKey ] = endpoint.args[ argKey ];
+						});
+					}
+				});
+			});
+		}
+	}
+
+	// Return the child node object as the new "level"
+	parentLevel[ levelKey ] = currentLevel;
+	return currentLevel.children;
+}
+
+/**
+ *
+ * @private
+ * @param {object}   namespaces The memo object that becomes a dictionary mapping API
+ *                              namespaces to an object of the namespace's routes
+ * @param {object}   routeObj   A route definition object
+ * @param {string}   route      The string key of the `routeObj` route object
+ * @returns {object} The namespaces dictionary memo object
+ */
+function reduceRouteTree( namespaces, routeObj, route ) {
+	var nsForRoute = routeObj.namespace;
+
+	// Strip the namespace from the route string (all routes should have the
+	// format `/namespace/other/stuff`) @TODO: Validate this assumption
+	// Also strip any trailing "/?": the slash is already optional and a single
+	// question mark would break the regex parser
+	var routeString = route.replace( '/' + nsForRoute + '/', '' ).replace( /\/\?$/, '' );
+
+	// Split the routes up into hierarchical route components
+	var routeComponents = splitPath( routeString );
+
+	// Do not make a namespace group for the API root
+	// Do not add the namespace root to its own group
+	// Do not take any action if routeString is empty
+	if ( ! nsForRoute || '/' + nsForRoute === route || ! routeString ) {
+		return namespaces;
+	}
+
+	// Ensure that the namespace object for this namespace exists
+	ensure( namespaces, nsForRoute, {} );
+
+	// Get a local reference to namespace object
+	var ns = namespaces[ nsForRoute ];
+
+	// The first element of the route tells us what type of resource this route
+	// is for, e.g. "posts" or "comments": we build one handler per resource
+	// type, so we group like resource paths together.
+	var resource = routeComponents[0];
+
+	// @TODO: This code above currently precludes baseless routes, e.g.
+	// myplugin/v2/(?P<resource>\w+) -- should those be supported?
+
+	// Create an array to represent this resource, and ensure it is assigned
+	// to the namespace object. The array will structure the "levels" (path
+	// components and subresource types) of this resource's endpoint handler.
+	ensure( ns, resource, {} );
+	var levels = ns[ resource ];
+
+	// Recurse through the route components, mutating levels with information about
+	// each child node encountered while walking through the routes tree and what
+	// arguments (parameters) are available for GET requests to this endpoint.
+	routeComponents.reduce( reduceRouteComponents.bind( null, routeObj, levels ), levels );
+
+	return namespaces;
+}
+
+/**
+ * Build a route tree by reducing over a routes definition object from the API
+ * root endpoint response object
+ *
+ * @method build
+ * @param {object} routes A dictionary of routes keyed by route regex strings
+ * @returns {object} A dictionary, keyed by namespace, of resource handler
+ * factory methods for each namespace's resources
+ */
+function buildRouteTree( routes ) {
+	return objectReduce( routes, reduceRouteTree, {} );
+}
+
+module.exports = {
+	build: buildRouteTree
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_alphanumeric-sort.js.html b/api-reference/wpapi/1.1.2/lib_util_alphanumeric-sort.js.html new file mode 100644 index 00000000..780910c1 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_alphanumeric-sort.js.html @@ -0,0 +1,81 @@ + + + + + + lib/util/alphanumeric-sort.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/alphanumeric-sort.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Utility function for sorting arrays of numbers or strings.
+ *
+ * @module util/alphanumeric-sort
+ * @param {String|Number} a The first comparator operand
+ * @param {String|Number} a The second comparator operand
+ * @returns -1 if the values are backwards, 1 if they're ordered, and 0 if they're the same
+ */
+function alphaNumericSort( a, b ) {
+	if ( a > b ) {
+		return 1;
+	}
+	if ( a < b ) {
+		return -1;
+	}
+	return 0;
+}
+
+module.exports = alphaNumericSort;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_apply-mixin.js.html b/api-reference/wpapi/1.1.2/lib_util_apply-mixin.js.html new file mode 100644 index 00000000..3ae799fc --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_apply-mixin.js.html @@ -0,0 +1,78 @@ + + + + + + lib/util/apply-mixin.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/apply-mixin.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Augment an object (specifically a prototype) with a mixin method
+ * (the provided object is mutated by reference)
+ *
+ * @module util/apply-mixin
+ * @param {Object} obj The object (usually a prototype) to augment
+ * @param {String} key The property to which the mixin method should be assigned
+ * @param {Function} mixin The mixin method
+ * @returns {void}
+ */
+module.exports = function( obj, key, mixin ) {
+	// Will not overwrite existing methods
+	if ( typeof mixin === 'function' && ! obj[ key ] ) {
+		obj[ key ] = mixin;
+	}
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_argument-is-numeric.js.html b/api-reference/wpapi/1.1.2/lib_util_argument-is-numeric.js.html new file mode 100644 index 00000000..0df6b355 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_argument-is-numeric.js.html @@ -0,0 +1,97 @@ + + + + + + lib/util/argument-is-numeric.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/argument-is-numeric.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Return true if the provided argument is a number, a numeric string, or an
+ * array of numbers or numeric strings
+ *
+ * @module util/argument-is-numeric
+ * @param {Number|String|Number[]|String[]} val The value to inspect
+ * @param {String} key The property to which the mixin method should be assigned
+ * @param {Function} mixin The mixin method
+ * @returns {void}
+ */
+function argumentIsNumeric( val ) {
+	if ( typeof val === 'number' ) {
+		return true;
+	}
+
+	if ( typeof val === 'string' ) {
+		return /^\d+$/.test( val );
+	}
+
+	if ( Array.isArray( val ) ) {
+		for ( var i = 0; i < val.length; i++ ) {
+			// Fail early if any argument isn't determined to be numeric
+			if ( ! argumentIsNumeric( val[ i ] ) ) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	// If it's not an array, and not a string, and not a number, we don't
+	// know what to do with it
+	return false;
+}
+
+module.exports = argumentIsNumeric;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_check-method-support.js.html b/api-reference/wpapi/1.1.2/lib_util_check-method-support.js.html new file mode 100644 index 00000000..bc6a8190 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_check-method-support.js.html @@ -0,0 +1,80 @@ + + + + + + lib/util/check-method-support.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/check-method-support.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Verify that a specific HTTP method is supported by the provided WPRequest
+ *
+ * @module util/check-method-support
+ * @param {String} method An HTTP method to check ('get', 'post', etc)
+ * @param {WPRequest} request A WPRequest object with a _supportedMethods array
+ * @returns true iff the method is within request._supportedMethods
+ */
+module.exports = function( method, request ) {
+	if ( request._supportedMethods.indexOf( method.toLowerCase() ) === -1 ) {
+		throw new Error(
+			'Unsupported method; supported methods are: ' +
+			request._supportedMethods.join( ', ' )
+		);
+	}
+
+	return true;
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_ensure.js.html b/api-reference/wpapi/1.1.2/lib_util_ensure.js.html new file mode 100644 index 00000000..7f770f32 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_ensure.js.html @@ -0,0 +1,77 @@ + + + + + + lib/util/ensure.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/ensure.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Ensure that a property is present in an object, initializing it to a default
+ * value if it is not already defined. Modifies the provided object by reference.
+ *
+ * @module util/ensure
+ * @param {object} obj              The object in which to ensure a property exists
+ * @param {string} prop             The property key to ensure
+ * @param {}       propDefaultValue The default value for the property
+ * @returns {void}
+ */
+module.exports = function( obj, prop, propDefaultValue ) {
+	if ( obj && obj[ prop ] === undefined ) {
+		obj[ prop ] = propDefaultValue;
+	}
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_is-empty-object.js.html b/api-reference/wpapi/1.1.2/lib_util_is-empty-object.js.html new file mode 100644 index 00000000..2efc1dab --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_is-empty-object.js.html @@ -0,0 +1,89 @@ + + + + + + lib/util/is-empty-object.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/is-empty-object.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Determine whether an provided value is an empty object
+ *
+ * @module util/is-empty-object
+ * @param {} value A value to test for empty-object-ness
+ * @returns {boolean} Whether the provided value is an empty object
+ */
+module.exports = function( value ) {
+	// If the value is not object-like, then it is certainly not an empty object
+	if ( typeof value !== 'object' ) {
+		return false;
+	}
+
+	// For our purposes an empty array should not be treated as an empty object
+	// (Since this is used to process invalid content-type responses, )
+	if ( Array.isArray( value ) ) {
+		return false;
+	}
+
+	for ( var key in value ) {
+		if ( value.hasOwnProperty( key ) ) {
+			return false;
+		}
+	}
+
+	return true;
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_key-val-to-obj.js.html b/api-reference/wpapi/1.1.2/lib_util_key-val-to-obj.js.html new file mode 100644 index 00000000..43a32910 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_key-val-to-obj.js.html @@ -0,0 +1,75 @@ + + + + + + lib/util/key-val-to-obj.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/key-val-to-obj.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Convert a (key, value) pair to a { key: value } object
+ *
+ * @module util/key-val-to-obj
+ * @param {string} key   The key to use in the returned object
+ * @param {}       value The value to assign to the provided key
+ * @returns {object} A dictionary object containing the key-value pair
+ */
+module.exports = function( key, value ) {
+	var obj = {};
+	obj[ key ] = value;
+	return obj;
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_named-group-regexp.js.html b/api-reference/wpapi/1.1.2/lib_util_named-group-regexp.js.html new file mode 100644 index 00000000..e4e6736e --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_named-group-regexp.js.html @@ -0,0 +1,101 @@ + + + + + + lib/util/named-group-regexp.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/named-group-regexp.js

+ + + + + + + +
+
+
/**
+ * @module util/named-group-regexp
+ */
+'use strict';
+
+var pattern = [
+	// Capture group start
+	'\\(\\?',
+	// Capture group name begins either `P<`, `<` or `'`
+	'(?:P<|<|\')',
+	// Everything up to the next `>`` or `'` (depending) will be the capture group name
+	'([^>\']+)',
+	// Capture group end
+	'[>\']',
+	// Get everything up to the end of the capture group: this is the RegExp used
+	// when matching URLs to this route, which we can use for validation purposes.
+	'([^\\)]*)',
+	// Capture group end
+	'\\)'
+].join( '' );
+
+var namedGroupRE = new RegExp( pattern );
+
+module.exports = {
+	/**
+	 * String representation of the exported Regular Expression; we construct this
+	 * RegExp from a string to enable more detailed annotation and permutation
+	 *
+	 * @prop {String} pattern
+	 */
+	pattern: pattern,
+
+	/**
+	 * Regular Expression to identify a capture group in PCRE formats
+	 * `(?<name>regex)`, `(?'name'regex)` or `(?P<name>regex)` (see
+	 * regular-expressions.info/refext.html)
+	 *
+	 * @prop {RegExp} namedGroupRE
+	 */
+	namedGroupRE: namedGroupRE
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_parameter-setter.js.html b/api-reference/wpapi/1.1.2/lib_util_parameter-setter.js.html new file mode 100644 index 00000000..e450a61c --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_parameter-setter.js.html @@ -0,0 +1,82 @@ + + + + + + lib/util/parameter-setter.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/parameter-setter.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * Helper to create a simple parameter setter convenience method
+ *
+ * @module util/parameter-setter
+ * @param {String} param The string key of the parameter this method will set
+ * @returns {Function} A setter method that can be assigned to a request instance
+ */
+module.exports = function( param ) {
+	/**
+	 * A setter for a specific parameter
+	 *
+	 * @chainable
+	 * @param {*} val The value to set for the the parameter
+	 * @returns The request instance on which this method was called (for chaining)
+	 */
+	return function( val ) {
+		/* jshint validthis:true */
+		return this.param( param, val );
+	};
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_util_split-path.js.html b/api-reference/wpapi/1.1.2/lib_util_split-path.js.html new file mode 100644 index 00000000..83b4bdc9 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_util_split-path.js.html @@ -0,0 +1,112 @@ + + + + + + lib/util/split-path.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/util/split-path.js

+ + + + + + + +
+
+
/**
+ * @module util/split-path
+ */
+'use strict';
+
+var namedGroupPattern = require( './named-group-regexp' ).pattern;
+
+// Convert capture groups to non-matching groups, because all capture groups
+// are included in the resulting array when an RE is passed to `.split()`
+// (We re-use the existing named group's capture pattern instead of creating
+// a new RegExp just for this purpose)
+var patternWithoutSubgroups = namedGroupPattern
+	.replace( /([^\\])\(([^?])/g, '$1(?:$2' );
+
+// Make a new RegExp using the same pattern as one single unified capture group,
+// so the match as a whole will be preserved after `.split()`. Permit non-slash
+// characters before or after the named capture group, although those components
+// will not yield functioning setters.
+var namedGroupRE = new RegExp( '([^/]*' + patternWithoutSubgroups + '[^/]*)' );
+
+/**
+ * Divide a route string up into hierarchical components by breaking it apart
+ * on forward slash characters.
+ *
+ * There are plugins (including Jetpack) that register routes with regex capture
+ * groups which also contain forward slashes, so those groups have to be pulled
+ * out first before the remainder of the string can be .split() as normal.
+ *
+ * @param {String} pathStr A route path string to break into components
+ * @returns {String[]} An array of route component strings
+ */
+module.exports = function( pathStr ) {
+	// Divide a string like "/some/path/(?P<with_named_groups>)/etc" into an
+	// array `[ "/some/path/", "(?P<with_named_groups>)", "/etc" ]`.
+	// Then, reduce through the array of parts, splitting any non-capture-group
+	// parts on forward slashes and discarding empty strings to create the final
+	// array of path components.
+	return pathStr.split( namedGroupRE ).reduce(function( components, part ) {
+		if ( ! part ) {
+			// Ignore empty strings parts
+			return components;
+		}
+
+		if ( namedGroupRE.test( part ) ) {
+			// Include named capture groups as-is
+			return components.concat( part );
+		}
+
+		// Split the part on / and filter out empty strings
+		return components.concat( part.split( '/' ).filter( Boolean ) );
+	}, [] );
+};
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/lib_wp-register-route.js.html b/api-reference/wpapi/1.1.2/lib_wp-register-route.js.html new file mode 100644 index 00000000..dcb48d26 --- /dev/null +++ b/api-reference/wpapi/1.1.2/lib_wp-register-route.js.html @@ -0,0 +1,166 @@ + + + + + + lib/wp-register-route.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

lib/wp-register-route.js

+ + + + + + + +
+
+
'use strict';
+
+var extend = require( 'node.extend' );
+
+var buildRouteTree = require( './route-tree' ).build;
+var generateEndpointFactories = require( './endpoint-factories' ).generate;
+var paramSetter = require( './util/parameter-setter' );
+var applyMixin = require( './util/apply-mixin' );
+var mixins = require( './mixins' );
+
+/**
+ * Create and return a handler for an arbitrary WP REST API endpoint.
+ *
+ * The first two parameters mirror `register_rest_route` in the REST API
+ * codebase:
+ *
+ * @memberof! WPAPI#
+ * @param {string}   namespace         A namespace string, e.g. 'myplugin/v1'
+ * @param {string}   restBase          A REST route string, e.g. '/author/(?P<id>\d+)'
+ * @param {object}   [options]         An (optional) options object
+ * @param {object}   [options.mixins]  A hash of functions to apply as mixins
+ * @param {string[]} [options.methods] An array of methods to whitelist (on the leaf node only)
+ * @returns {Function} An endpoint handler factory function for the specified route
+ */
+function registerRoute( namespace, restBase, options ) {
+	// Support all methods until requested to do otherwise
+	var supportedMethods = [ 'head', 'get', 'patch', 'put', 'post', 'delete' ];
+
+	if ( options && Array.isArray( options.methods ) ) {
+		// Permit supported methods to be specified as an array
+		supportedMethods = options.methods.map(function( method ) {
+			return method.trim().toLowerCase();
+		});
+	} else if ( options && typeof options.methods === 'string' ) {
+		// Permit a supported method to be specified as a string
+		supportedMethods = [ options.methods.trim().toLowerCase() ];
+	}
+
+	// Ensure that if GET is supported, then HEAD is as well, and vice-versa
+	if ( supportedMethods.indexOf( 'get' ) !== -1 && supportedMethods.indexOf( 'head' ) === -1 ) {
+		supportedMethods.push( 'head' );
+	} else if ( supportedMethods.indexOf( 'head' ) !== -1 && supportedMethods.indexOf( 'get' ) === -1 ) {
+		supportedMethods.push( 'get' );
+	}
+
+	var fullRoute = namespace
+		// Route should always have preceding slash
+		.replace( /^[\s/]*/, '/' )
+		// Route should always be joined to namespace with a single slash
+		.replace( /[\s/]*$/, '/' ) + restBase.replace( /^[\s/]*/, '' );
+
+	var routeObj = {};
+	routeObj[ fullRoute ] = {
+		namespace: namespace,
+		methods: supportedMethods
+	};
+
+	// Go through the same steps used to bootstrap the client to parse the
+	// provided route out into a handler request method
+	var routeTree = buildRouteTree( routeObj );
+	// Parse the mock route object into endpoint factories
+	var endpointFactories = generateEndpointFactories( routeTree )[ namespace ];
+	var EndpointRequest = endpointFactories[ Object.keys( endpointFactories )[ 0 ] ].Ctor;
+
+	if ( options && options.params ) {
+		options.params.forEach(function( param ) {
+			// Only accept string parameters
+			if ( typeof param !== 'string' ) {
+				return;
+			}
+
+			// If the parameter can be mapped to a mixin, apply that mixin
+			if ( typeof mixins[ param ] === 'object' ) {
+				Object.keys( mixins[ param ] ).forEach(function( key ) {
+					applyMixin( EndpointRequest.prototype, key, mixins[ param ][ key ] );
+				});
+				return;
+			}
+
+			// Attempt to create a simple setter for any parameters for which
+			// we do not already have a custom mixin
+			applyMixin( EndpointRequest.prototype, param, paramSetter( param ) );
+		});
+	}
+
+	// Set any explicitly-provided object mixins
+	if ( options && typeof options.mixins === 'object' ) {
+
+		// Set any specified mixin functions on the response
+		Object.keys( options.mixins ).forEach(function( key ) {
+			applyMixin( EndpointRequest.prototype, key, options.mixins[ key ] );
+		});
+	}
+
+	function endpointFactory( options ) {
+		/* jshint validthis:true */
+		options = options || {};
+		options = extend( options, this && this._options );
+		return new EndpointRequest( options );
+	}
+	endpointFactory.Ctor = EndpointRequest;
+
+	return endpointFactory;
+}
+
+module.exports = registerRoute;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/api-reference/wpapi/1.1.2/module-autodiscovery.html b/api-reference/wpapi/1.1.2/module-autodiscovery.html new file mode 100644 index 00000000..51f20b9a --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-autodiscovery.html @@ -0,0 +1,334 @@ + + + + + + autodiscovery - Documentation + + + + + + + + + + + + + + + + + +
+ +

autodiscovery

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Utility methods used when querying a site in order to discover its available +API endpoints

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(inner) locateAPIRootHeader(response) → {String}

+ + + + + +
+

Attempt to locate a rel="https://api.w.org" link relation header

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
response + + +Object + + + + +

A response object with a link or headers property

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +String + + +
+
+ + +
+

The URL of the located API root

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-endpoint-factories.html b/api-reference/wpapi/1.1.2/module-endpoint-factories.html new file mode 100644 index 00000000..2c8284be --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-endpoint-factories.html @@ -0,0 +1,366 @@ + + + + + + endpoint-factories - Documentation + + + + + + + + + + + + + + + + + +
+ +

endpoint-factories

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Take a WP route string (with PCRE named capture groups), such as /author/(?P<id>\d+), +and generate request handler factory methods for each represented endpoint.

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(inner) generate(namespace, routesByNamespace) → {object}

+ + + + + +
+

Given an array of route definitions and a specific namespace for those routes, +recurse through the node tree representing all possible routes within the +provided namespace to define path value setters (and corresponding property +validators) for all possible variants of each resource's API endpoints.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
namespace + + +string + + + + +

The namespace string for these routes

+ +
routesByNamespace + + +object + + + + +

A dictionary of namespace - route definition + object pairs as generated from buildRouteTree, + where each route definition object is a dictionary + keyed by route definition strings

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + +
+

A dictionary of endpoint request handler factories

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-endpoint-request.html b/api-reference/wpapi/1.1.2/module-endpoint-request.html new file mode 100644 index 00000000..e1d52c9c --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-endpoint-request.html @@ -0,0 +1,308 @@ + + + + + + endpoint-request - Documentation + + + + + + + + + + + + + + + + + +
+ +

endpoint-request

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(inner) create(handlerSpec, resource, namespace) → {function}

+ + + + + +
+

Create an endpoint request handler constructor for a specific resource tree

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
handlerSpec + + +Object + + + + +

A resource handler specification object

+ +
resource + + +String + + + + +

The root resource of requests created from the returned factory

+ +
namespace + + +String + + + + +

The namespace string for the returned factory's handlers

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +function + + +
+
+ + +
+

A constructor inheriting from WPRequest

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-http-transport.html b/api-reference/wpapi/1.1.2/module-http-transport.html new file mode 100644 index 00000000..cb4860b5 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-http-transport.html @@ -0,0 +1,1214 @@ + + + + + + http-transport - Documentation + + + + + + + + + + + + + + + + + +
+ +

http-transport

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(inner) delete(wpreq, dataopt, callbackopt) → {Promise}

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
wpreq + + +WPRequest + + + + + + + + + + +

A WPRequest query object

+ +
data + + +Object + + + + + + <optional>
+ + + + + +
+

Data to send along with the DELETE request

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the DELETE request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

(inner) get(wpreq, callbackopt) → {Promise}

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
wpreq + + +WPRequest + + + + + + + + + + +

A WPRequest query object

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the GET request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

(inner) head(wpreq, callbackopt) → {Promise}

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
wpreq + + +WPRequest + + + + + + + + + + +

A WPRequest query object

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the HEAD request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the header results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

(inner) post(wpreq, data, callbackopt) → {Promise}

+ + + + + +
+

Invoke an HTTP "POST" request against the provided endpoint

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
wpreq + + +WPRequest + + + + + + + + + + +

A WPRequest query object

+ +
data + + +Object + + + + + + + + + + +

The data for the POST request

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the POST request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + +
+ + + +

(inner) put(wpreq, data, callbackopt) → {Promise}

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
wpreq + + +WPRequest + + + + + + + + + + +

A WPRequest query object

+ +
data + + +Object + + + + + + + + + + +

The data for the PUT request

+ +
callback + + +function + + + + + + <optional>
+ + + + + +
+

A callback to invoke with the results of the PUT request

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + +
+

A promise to the results of the HTTP request

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-mixins_filters-filters.html b/api-reference/wpapi/1.1.2/module-mixins_filters-filters.html new file mode 100644 index 00000000..36eff93e --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-mixins_filters-filters.html @@ -0,0 +1,145 @@ + + + + + + filters - Documentation + + + + + + + + + + + + + + + + + +
+ +

filters

+ + + + + + + +
+ +
+ +

+ mixins/filters~ + + filters +

+ + +
+ +
+
+ + +

Filter methods that can be mixed in to a request constructor's prototype to +allow that request to take advantage of the ?filter[]= aliases for WP_Query +parameters for collection endpoints, when available.

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-mixins_filters.html b/api-reference/wpapi/1.1.2/module-mixins_filters.html new file mode 100644 index 00000000..e289d36a --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-mixins_filters.html @@ -0,0 +1,1101 @@ + + + + + + mixins/filters - Documentation + + + + + + + + + + + + + + + + + +
+ +

mixins/filters

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + +

Mixins

+ +
+
filters
+
+
+ + + + + + + +

Methods

+ + + +
+ + + +

(inner) day(day)

+ + + + + +
+

Add the day filter into the request to retrieve posts for a given day

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
day + + +Number + + + + +

Integer representation of the day requested

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) filter(props, valueopt)

+ + + + + +
+

Specify key-value pairs by which to filter the API results (commonly used +to retrieve only posts meeting certain criteria, such as posts within a +particular category or by a particular author).

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
props + + +String +| + +Object + + + + + + + + + + +

A filter property name string, or object of name/value pairs

+ +
value + + +String +| + +Number +| + +Array + + + + + + <optional>
+ + + + + +
+

The value(s) corresponding to the provided filter property

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+
Example
+ +
// Set a single property:
+    wp.filter( 'post_type', 'cpt_event' )...
+
+    // Set multiple properties at once:
+    wp.filter({
+        post_status: 'publish',
+        category_name: 'news'
+    })...
+
+    // Chain calls to .filter():
+    wp.filter( 'post_status', 'publish' ).filter( 'category_name', 'news' )...
+ +
+ +
+ + +
+ + + +

(inner) month(month)

+ + + + + +
+

Query for posts published in a given month, either by providing the number +of the requested month (e.g. 3), or the month's name as a string (e.g. "March")

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
month + + +Number +| + +String + + + + +

Integer for month (1) or month string ("January")

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) path(path)

+ + + + + +
+

Specify that we are requesting a page by its path (specific to Page resources)

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
path + + +String + + + + +

The root-relative URL path for a page

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) taxonomy(taxonomy, term)

+ + + + + +
+

Restrict the query results to posts matching one or more taxonomy terms.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
taxonomy + + +String + + + + +

The name of the taxonomy to filter by

+ +
term + + +String +| + +Number +| + +Array + + + + +

A string or integer, or array thereof, representing terms

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) year(year)

+ + + + + +
+

Query for posts published in a given year.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
year + + +Number + + + + +

integer representation of year requested

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-mixins_parameters-parameters.html b/api-reference/wpapi/1.1.2/module-mixins_parameters-parameters.html new file mode 100644 index 00000000..eab3b076 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-mixins_parameters-parameters.html @@ -0,0 +1,141 @@ + + + + + + parameters - Documentation + + + + + + + + + + + + + + + + + +
+ +

parameters

+ + + + + + + +
+ +
+ +

+ mixins/parameters~ + + parameters +

+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-mixins_parameters.html b/api-reference/wpapi/1.1.2/module-mixins_parameters.html new file mode 100644 index 00000000..e67bac04 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-mixins_parameters.html @@ -0,0 +1,2365 @@ + + + + + + mixins/parameters - Documentation + + + + + + + + + + + + + + + + + +
+ +

mixins/parameters

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Filter methods that can be mixed in to a request constructor's prototype to +allow that request to take advantage of top-level query parameters for +collection endpoints. These are most relevant to posts, pages and CPTs, but +pagination helpers are applicable to any collection.

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + +

Mixins

+ +
+
parameters
+
+
+ + + + + + + +

Methods

+ + + +
+ + + +

(inner) after(date)

+ + + + + +
+

Retrieve only records published after a specified date

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +String +| + +Date + + + + +

An ISO 8601-compliant date string, or Date object

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+
Examples
+ +

Provide an ISO 8601-compliant date string

+ +
    wp.posts().after('1986-03-22')...
+ +

Provide a JavaScript Date object

+ +
    wp.posts().after( new Date( 1986, 03, 22 ) )...
+ +
+ +
+ + +
+ + + +

(inner) author(author)

+ + + + + +
+

Query for posts by a specific author. +This method will replace any previous 'author' query parameters that had been set.

+

Note that this method will either set the "author" top-level query parameter, +or else the "author_name" filter parameter (when querying by nicename): this is +irregular as most parameter helper methods either set a top level parameter or a +filter, not both.

+

Usage with the author nicename string is deprecated. Query by author ID instead. +If the "rest-filter" plugin is not installed, the name query will have no effect.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
author + + +String +| + +Number + + + + +

The nicename or ID for a particular author

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) before(date)

+ + + + + +
+

Retrieve only records published before a specified date

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +String +| + +Date + + + + +

An ISO 8601-compliant date string, or Date object

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+
Examples
+ +

Provide an ISO 8601-compliant date string

+ +
    wp.posts().before('2016-03-22')...
+ +

Provide a JavaScript Date object

+ +
    wp.posts().before( new Date( 2016, 03, 22 ) )...
+ +
+ +
+ + +
+ + + +

(inner) categories(categories)

+ + + + + +
+

Retrieve only records associated with one of the provided categories

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
categories + + +String +| + +Number +| + +Array + + + + +

A term ID integer or numeric string, or array thereof

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) excludeCategories(category)

+ + + + + +
+

Exclude records associated with any of the provided category IDs

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
category + + +String +| + +Number +| + +Array + + + + +

A term ID integer or numeric string, or array thereof

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) excludeTags(category)

+ + + + + +
+

Exclude records associated with any of the provided tag IDs

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
category + + +String +| + +Number +| + +Array + + + + +

A term ID integer or numeric string, or array thereof

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) parent(parentId)

+ + + + + +
+

Search for hierarchical taxonomy terms that are children of the parent term +indicated by the provided term ID

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
parentId + + +Number + + + + +

The ID of a (hierarchical) taxonomy term

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+
Example
+ +
wp.pages().parent( 3 ).then(function( pages ) {
+      // console.log( 'all of these pages are nested below page ID#3:' );
+      // console.log( pages );
+    });
+
+    wp.categories().parent( 42 ).then(function( categories ) {
+      console.log( 'all of these categories are sub-items of cat ID#42:' );
+      console.log( categories );
+    });
+ +
+ +
+ + +
+ + + +

(inner) password(password)

+ + + + + +
+

Specify the password to use to access the content of a password-protected post

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
password + + +string + + + + +

A string password to access protected content within a post

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) post(post)

+ + + + + +
+

Specify the post for which to retrieve terms (relevant for e.g. taxonomy +and comment collection endpoints).

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
post + + +String +| + +Number + + + + +

The ID of the post for which to retrieve terms

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) status(status)

+ + + + + +
+

Specify for which post statuses to return posts in a response collection

+

See https://codex.wordpress.org/Post_Status -- the default post status +values in WordPress which are most relevant to the API are 'publish', +'future', 'draft', 'pending', 'private', and 'trash'. This parameter also +supports passing the special value "any" to return all statuses.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
status + + +string +| + +Array.<string> + + + + +

A status name string or array of strings

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) sticky(sticky)

+ + + + + +
+

Specify whether to return only, or to completely exclude, sticky posts

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
sticky + + +boolean + + + + +

A boolean value for whether ONLY sticky posts (true) or + NO sticky posts (false) should be returned in the query

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) tag(tag)

+ + + + + +
+

Legacy wrapper for .categories() that uses ?filter to query by slug if available

+
+ + + + + +
+ + + + + + + + + + + + + + + + +
Deprecated:
  • Use `.categories()` and query by category IDs
+ + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tag + + +String +| + +Number +| + +Array + + + + +

A category term slug string, numeric ID, or array of numeric IDs

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) tag(tag)

+ + + + + +
+

Legacy wrapper for .tags() that uses ?filter to query by slug if available

+
+ + + + + +
+ + + + + + + + + + + + + + + + +
Deprecated:
  • Use `.tags()` and query by term IDs
+ + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tag + + +String +| + +Number +| + +Array + + + + +

A tag term slug string, numeric ID, or array of numeric IDs

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + +
+ + + +

(inner) tags(tags)

+ + + + + +
+

Retrieve only records associated with one of the provided tag IDs

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tags + + +String +| + +Number +| + +Array + + + + +

A term ID integer or numeric string, or array thereof

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

The request instance (for chaining)

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-path-part-setter.html b/api-reference/wpapi/1.1.2/module-path-part-setter.html new file mode 100644 index 00000000..58e8dc59 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-path-part-setter.html @@ -0,0 +1,92 @@ + + + + + + path-part-setter - Documentation + + + + + + + + + + + + + + + + + +
+ +

path-part-setter

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-resource-handler-spec.html b/api-reference/wpapi/1.1.2/module-resource-handler-spec.html new file mode 100644 index 00000000..db6693e3 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-resource-handler-spec.html @@ -0,0 +1,160 @@ + + + + + + resource-handler-spec - Documentation + + + + + + + + + + + + + + + + + +
+ +

resource-handler-spec

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + +

Members

+ + + +
+

(inner) create

+ + + + +
+

Create a node handler specification object from a route definition object

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-route-tree.html b/api-reference/wpapi/1.1.2/module-route-tree.html new file mode 100644 index 00000000..49d3df3f --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-route-tree.html @@ -0,0 +1,258 @@ + + + + + + route-tree - Documentation + + + + + + + + + + + + + + + + + +
+ +

route-tree

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(inner) build(routes) → {object}

+ + + + + +
+

Build a route tree by reducing over a routes definition object from the API +root endpoint response object

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
routes + + +object + + + + +

A dictionary of routes keyed by route regex strings

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + +
+

A dictionary, keyed by namespace, of resource handler +factory methods for each namespace's resources

+
+ + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_alphanumeric-sort.html b/api-reference/wpapi/1.1.2/module-util_alphanumeric-sort.html new file mode 100644 index 00000000..58f60ffe --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_alphanumeric-sort.html @@ -0,0 +1,266 @@ + + + + + + util/alphanumeric-sort - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/alphanumeric-sort

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Utility function for sorting arrays of numbers or strings.

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
a + + +String +| + +Number + + + + +

The first comparator operand

+ +
a + + +String +| + +Number + + + + +

The second comparator operand

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

-1 if the values are backwards, 1 if they're ordered, and 0 if they're the same

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_apply-mixin.html b/api-reference/wpapi/1.1.2/module-util_apply-mixin.html new file mode 100644 index 00000000..d564b319 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_apply-mixin.html @@ -0,0 +1,295 @@ + + + + + + util/apply-mixin - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/apply-mixin

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Augment an object (specifically a prototype) with a mixin method +(the provided object is mutated by reference)

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
obj + + +Object + + + + +

The object (usually a prototype) to augment

+ +
key + + +String + + + + +

The property to which the mixin method should be assigned

+ +
mixin + + +function + + + + +

The mixin method

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +void + + +
+
+ + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_argument-is-numeric.html b/api-reference/wpapi/1.1.2/module-util_argument-is-numeric.html new file mode 100644 index 00000000..786d7d3c --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_argument-is-numeric.html @@ -0,0 +1,304 @@ + + + + + + util/argument-is-numeric - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/argument-is-numeric

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Return true if the provided argument is a number, a numeric string, or an +array of numbers or numeric strings

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
val + + +Number +| + +String +| + +Array.<Number> +| + +Array.<String> + + + + +

The value to inspect

+ +
key + + +String + + + + +

The property to which the mixin method should be assigned

+ +
mixin + + +function + + + + +

The mixin method

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +void + + +
+
+ + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_check-method-support.html b/api-reference/wpapi/1.1.2/module-util_check-method-support.html new file mode 100644 index 00000000..ab80f766 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_check-method-support.html @@ -0,0 +1,260 @@ + + + + + + util/check-method-support - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/check-method-support

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Verify that a specific HTTP method is supported by the provided WPRequest

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
method + + +String + + + + +

An HTTP method to check ('get', 'post', etc)

+ +
request + + +WPRequest + + + + +

A WPRequest object with a _supportedMethods array

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + + +
+

true iff the method is within request._supportedMethods

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_ensure.html b/api-reference/wpapi/1.1.2/module-util_ensure.html new file mode 100644 index 00000000..898aff22 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_ensure.html @@ -0,0 +1,290 @@ + + + + + + util/ensure - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/ensure

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Ensure that a property is present in an object, initializing it to a default +value if it is not already defined. Modifies the provided object by reference.

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
obj + + +object + + + + +

The object in which to ensure a property exists

+ +
prop + + +string + + + + +

The property key to ensure

+ +
propDefaultValue + + +

The default value for the property

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +void + + +
+
+ + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_is-empty-object.html b/api-reference/wpapi/1.1.2/module-util_is-empty-object.html new file mode 100644 index 00000000..2a10f3d6 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_is-empty-object.html @@ -0,0 +1,241 @@ + + + + + + util/is-empty-object - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/is-empty-object

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Determine whether an provided value is an empty object

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
value + + +

A value to test for empty-object-ness

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + +
+

Whether the provided value is an empty object

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_key-val-to-obj.html b/api-reference/wpapi/1.1.2/module-util_key-val-to-obj.html new file mode 100644 index 00000000..85a57f8c --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_key-val-to-obj.html @@ -0,0 +1,267 @@ + + + + + + util/key-val-to-obj - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/key-val-to-obj

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Convert a (key, value) pair to a { key: value } object

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + + +

The key to use in the returned object

+ +
value + + +

The value to assign to the provided key

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + +
+

A dictionary object containing the key-value pair

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_named-group-regexp.html b/api-reference/wpapi/1.1.2/module-util_named-group-regexp.html new file mode 100644 index 00000000..e13126ea --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_named-group-regexp.html @@ -0,0 +1,335 @@ + + + + + + util/named-group-regexp - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/named-group-regexp

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + +

Members

+ + + +
+

(static) namedGroupRE

+ + + + +
+

Regular Expression to identify a capture group in PCRE formats +(?<name>regex), (?'name'regex) or (?P<name>regex) (see +regular-expressions.info/refext.html)

+
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
namedGroupRE + + +RegExp + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

(static) pattern

+ + + + +
+

String representation of the exported Regular Expression; we construct this +RegExp from a string to enable more detailed annotation and permutation

+
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
pattern + + +String + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_parameter-setter.html b/api-reference/wpapi/1.1.2/module-util_parameter-setter.html new file mode 100644 index 00000000..0aea3be4 --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_parameter-setter.html @@ -0,0 +1,246 @@ + + + + + + util/parameter-setter - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/parameter-setter

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + +

Helper to create a simple parameter setter convenience method

+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
param + + +String + + + + +

The string key of the parameter this method will set

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +function + + +
+
+ + +
+

A setter method that can be assigned to a request instance

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/module-util_split-path.html b/api-reference/wpapi/1.1.2/module-util_split-path.html new file mode 100644 index 00000000..0a9dbffd --- /dev/null +++ b/api-reference/wpapi/1.1.2/module-util_split-path.html @@ -0,0 +1,258 @@ + + + + + + util/split-path - Documentation + + + + + + + + + + + + + + + + + +
+ +

util/split-path

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("util/split-path"))(pathStr) → {Array.<String>}

+ + + + + +
+

Divide a route string up into hierarchical components by breaking it apart +on forward slash characters.

+

There are plugins (including Jetpack) that register routes with regex capture +groups which also contain forward slashes, so those groups have to be pulled +out first before the remainder of the string can be .split() as normal.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
pathStr + + +String + + + + +

A route path string to break into components

+ +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Array.<String> + + +
+
+ + +
+

An array of route component strings

+
+ + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/api-reference/wpapi/1.1.2/scripts/linenumber.js b/api-reference/wpapi/1.1.2/scripts/linenumber.js new file mode 100644 index 00000000..8d52f7ea --- /dev/null +++ b/api-reference/wpapi/1.1.2/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/api-reference/wpapi/1.1.2/scripts/prettify/Apache-License-2.0.txt b/api-reference/wpapi/1.1.2/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/api-reference/wpapi/1.1.2/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/api-reference/wpapi/1.1.2/scripts/prettify/lang-css.js b/api-reference/wpapi/1.1.2/scripts/prettify/lang-css.js new file mode 100644 index 00000000..041e1f59 --- /dev/null +++ b/api-reference/wpapi/1.1.2/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/api-reference/wpapi/1.1.2/scripts/prettify/prettify.js b/api-reference/wpapi/1.1.2/scripts/prettify/prettify.js new file mode 100644 index 00000000..eef5ad7e --- /dev/null +++ b/api-reference/wpapi/1.1.2/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p code { + font-size: 0.85em; +} + +.readme table { + margin-bottom: 1em; + border-collapse: collapse; + border-spacing: 0; +} + +.readme table tr { + background-color: #fff; + border-top: 1px solid #ccc; +} + +.readme table th, +.readme table td { + padding: 6px 13px; + border: 1px solid #ddd; +} + +.readme table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +/** Nav **/ +nav { + float: left; + display: block; + width: 250px; + background: #fff; + overflow: auto; + position: fixed; + height: 100%; + padding: 10px; + border-right: 1px solid #eee; + /* box-shadow: 0 0 3px rgba(0,0,0,0.1); */ +} + +nav li { + list-style: none; + padding: 0; + margin: 0; +} + +.nav-heading { + margin-top: 10px; + font-weight: bold; +} + +.nav-heading a { + color: #888; + font-size: 14px; + display: inline-block; +} + +.nav-item-type { + /* margin-left: 5px; */ + width: 18px; + height: 18px; + display: inline-block; + text-align: center; + border-radius: 0.2em; + margin-right: 5px; + font-weight: bold; + line-height: 20px; + font-size: 13px; +} + +.type-function { + background: #B3E5FC; + color: #0288D1; +} + +.type-class { + background: #D1C4E9; + color: #4527A0; +} + +.type-member { + background: #C8E6C9; + color: #388E3C; +} + +.type-module { + background: #E1BEE7; + color: #7B1FA2; +} + + +/** Footer **/ +footer { + color: hsl(0, 0%, 28%); + margin-left: 250px; + display: block; + padding: 30px; + font-style: italic; + font-size: 90%; + border-top: 1px solid #eee; +} + +.ancestors { + color: #999 +} + +.ancestors a { + color: #999 !important; + text-decoration: none; +} + +.clear { + clear: both +} + +.important { + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px +} + +.type-signature { + color: #aaa +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace +} + +.details { + margin-top: 14px; + border-left: 2px solid #DDD; + line-height: 30px; +} + +.details dt { + width: 120px; + float: left; + padding-left: 10px; +} + +.details dd { + margin-left: 70px +} + +.details ul { + margin: 0 +} + +.details ul { + list-style-type: none +} + +.details li { + margin-left: 30px +} + +.details pre.prettyprint { + margin: 0 +} + +.details .object-value { + padding-top: 0 +} + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption { + font-style: italic; + font-size: 107%; + margin: 0; +} + +.prettyprint { + font-size: 13px; + border: 1px solid #ddd; + border-radius: 3px; + box-shadow: 0 1px 3px hsla(0, 0%, 0%, 0.05); + overflow: auto; +} + +.prettyprint.source { + width: inherit +} + +.prettyprint code { + font-size: 12px; + line-height: 18px; + display: block; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code:empty:before { + content: ''; +} + +.prettyprint > code { + padding: 15px +} + +.prettyprint .linenums code { + padding: 0 15px +} + +.prettyprint .linenums li:first-of-type code { + padding-top: 15px +} + +.prettyprint code span.line { + display: inline-block +} + +.prettyprint.linenums { + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol { + padding-left: 0 +} + +.prettyprint.linenums li { + border-left: 3px #ddd solid +} + +.prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { + background-color: lightyellow +} + +.prettyprint.linenums li * { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params, .props { + border-spacing: 0; + border: 1px solid #ddd; + border-collapse: collapse; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + width: 100%; + font-size: 14px; + /* margin-left: 15px; */ +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td, .params th, .props td, .props th { + margin: 0px; + text-align: left; + vertical-align: top; + padding: 10px; + display: table-cell; +} + +.params td { + border-top: 1px solid #eee +} + +.params thead tr, .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params .params thead tr, .props .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params td.description > p:first-child, .props td.description > p:first-child { + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, .props td.description > p:last-child { + margin-bottom: 0; + padding-bottom: 0; +} + +dl.param-type { + /* border-bottom: 1px solid hsl(0, 0%, 87%); */ + margin: 0; + padding: 0; + font-size: 16px; +} + +.param-type dt, .param-type dd { + display: inline-block +} + +.param-type dd { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + display: inline-block; + padding: 0; + margin: 0; + font-size: 14px; +} + +.disabled { + color: #454545 +} + +/* navicon button */ +.navicon-button { + display: none; + position: relative; + padding: 2.0625rem 1.5rem; + transition: 0.25s; + cursor: pointer; + user-select: none; + opacity: .8; +} +.navicon-button .navicon:before, .navicon-button .navicon:after { + transition: 0.25s; +} +.navicon-button:hover { + transition: 0.5s; + opacity: 1; +} +.navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { + transition: 0.25s; +} +.navicon-button:hover .navicon:before { + top: .825rem; +} +.navicon-button:hover .navicon:after { + top: -.825rem; +} + +/* navicon */ +.navicon { + position: relative; + width: 2.5em; + height: .3125rem; + background: #000; + transition: 0.3s; + border-radius: 2.5rem; +} +.navicon:before, .navicon:after { + display: block; + content: ""; + height: .3125rem; + width: 2.5rem; + background: #000; + position: absolute; + z-index: -1; + transition: 0.3s 0.25s; + border-radius: 1rem; +} +.navicon:before { + top: .625rem; +} +.navicon:after { + top: -.625rem; +} + +/* open */ +.nav-trigger:checked + label:not(.steps) .navicon:before, +.nav-trigger:checked + label:not(.steps) .navicon:after { + top: 0 !important; +} + +.nav-trigger:checked + label .navicon:before, +.nav-trigger:checked + label .navicon:after { + transition: 0.5s; +} + +/* Minus */ +.nav-trigger:checked + label { + transform: scale(0.75); +} + +/* × and + */ +.nav-trigger:checked + label.plus .navicon, +.nav-trigger:checked + label.x .navicon { + background: transparent; +} + +.nav-trigger:checked + label.plus .navicon:before, +.nav-trigger:checked + label.x .navicon:before { + transform: rotate(-45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus .navicon:after, +.nav-trigger:checked + label.x .navicon:after { + transform: rotate(45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus { + transform: scale(0.75) rotate(45deg); +} + +.nav-trigger:checked ~ nav { + left: 0 !important; +} + +.nav-trigger:checked ~ .overlay { + display: block; +} + +.nav-trigger { + position: fixed; + top: 0; + clip: rect(0, 0, 0, 0); +} + +.overlay { + display: none; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: hsla(0, 0%, 0%, 0.5); + z-index: 1; +} + +.section-method { + margin-bottom: 30px; + padding-bottom: 30px; + border-bottom: 1px solid #eee; +} + +@media only screen and (min-width: 320px) and (max-width: 680px) { + body { + overflow-x: hidden; + } + + nav { + background: #FFF; + width: 250px; + height: 100%; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: -250px; + z-index: 3; + padding: 0 10px; + transition: left 0.2s; + } + + .navicon-button { + display: inline-block; + position: fixed; + top: 1.5em; + right: 0; + z-index: 2; + } + + #main { + width: 100%; + min-width: 360px; + } + + #main h1.page-title { + margin: 1em 0; + } + + #main section { + padding: 0; + } + + footer { + margin-left: 0; + } +} + +@media only print { + nav { + display: none; + } + + #main { + float: none; + width: 100%; + } +} diff --git a/api-reference/wpapi/1.1.2/styles/prettify-jsdoc.css b/api-reference/wpapi/1.1.2/styles/prettify-jsdoc.css new file mode 100644 index 00000000..834a866d --- /dev/null +++ b/api-reference/wpapi/1.1.2/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: hsl(104, 100%, 24%); + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/api-reference/wpapi/1.1.2/styles/prettify-tomorrow.css b/api-reference/wpapi/1.1.2/styles/prettify-tomorrow.css new file mode 100644 index 00000000..81e74d13 --- /dev/null +++ b/api-reference/wpapi/1.1.2/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: hsl(104, 100%, 24%); } + + /* a keyword */ + .kwd { + color: hsl(240, 100%, 50%); } + + /* a comment */ + .com { + color: hsl(0, 0%, 60%); } + + /* a type name */ + .typ { + color: hsl(240, 100%, 32%); } + + /* a literal value */ + .lit { + color: hsl(240, 100%, 40%); } + + /* punctuation */ + .pun { + color: #000000; } + + /* lisp open bracket */ + .opn { + color: #000000; } + + /* lisp close bracket */ + .clo { + color: #000000; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/api-reference/wpapi/1.1.2/wpapi-computed-methods.jsdoc.html b/api-reference/wpapi/1.1.2/wpapi-computed-methods.jsdoc.html new file mode 100644 index 00000000..f0dabaf2 --- /dev/null +++ b/api-reference/wpapi/1.1.2/wpapi-computed-methods.jsdoc.html @@ -0,0 +1,137 @@ + + + + + + wpapi-computed-methods.jsdoc - Documentation + + + + + + + + + + + + + + + + + +
+ +

wpapi-computed-methods.jsdoc

+ + + + + + + +
+
+
/**
+ * Start a request against /posts endpoints
+ *
+ * @method posts
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /pages endpoints
+ *
+ * @method pages
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /types endpoints
+ *
+ * @method types
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /comments endpoints
+ *
+ * @method comments
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /taxonomies endpoints
+ *
+ * @method taxonomies
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /tags endpoint
+ *
+ * @method tags
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /categories endpoint
+ *
+ * @method categories
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /statuses endpoints
+ *
+ * @method statuses
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /users endpoints
+ *
+ * @method users
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /media endpoints
+ *
+ * @method media
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+/**
+ * Start a request against /settings endpoint
+ *
+ * @method settings
+ * @memberof! WPAPI
+ * @returns {WPRequest}
+ */
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.4.3 on Thu Jul 06 2017 14:09:15 GMT-0400 (EDT) using the Minami theme. +
+ + + + + diff --git a/api-reference/wpapi/1.1.2/wpapi.js.html b/api-reference/wpapi/1.1.2/wpapi.js.html new file mode 100644 index 00000000..6e1e7de5 --- /dev/null +++ b/api-reference/wpapi/1.1.2/wpapi.js.html @@ -0,0 +1,523 @@ + + + + + + wpapi.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

wpapi.js

+ + + + + + + +
+
+
/**
+ * A WP REST API client for Node.js
+ *
+ * @example
+ *     var wp = new WPAPI({ endpoint: 'http://src.wordpress-develop.dev/wp-json' });
+ *     wp.posts().then(function( posts ) {
+ *         console.log( posts );
+ *     }).catch(function( err ) {
+ *         console.error( err );
+ *     });
+ *
+ * @license MIT
+ })
+ */
+'use strict';
+
+var extend = require( 'node.extend' );
+var objectReduce = require( './lib/util/object-reduce' );
+
+// This JSON file provides enough data to create handler methods for all valid
+// API routes in WordPress 4.7
+var defaultRoutes = require( './lib/data/default-routes.json' );
+var buildRouteTree = require( './lib/route-tree' ).build;
+var generateEndpointFactories = require( './lib/endpoint-factories' ).generate;
+
+// The default endpoint factories will be lazy-loaded by parsing the default
+// route tree data if a default-mode WPAPI instance is created (i.e. one that
+// is to be bootstrapped with the handlers for all of the built-in routes)
+var defaultEndpointFactories;
+
+// Constant used to detect first-party WordPress REST API routes
+var apiDefaultNamespace = 'wp/v2';
+
+// Pull in autodiscovery methods
+var autodiscovery = require( './lib/autodiscovery' );
+
+// Pull in base module constructors
+var WPRequest = require( './lib/constructors/wp-request' );
+
+// Pull in default HTTP transport
+var httpTransport = require( './lib/http-transport' );
+
+/**
+ * Construct a REST API client instance object to create
+ *
+ * @constructor WPAPI
+ * @param {Object} options             An options hash to configure the instance
+ * @param {String} options.endpoint    The URI for a WP-API endpoint
+ * @param {String} [options.username]  A WP-API Basic Auth username
+ * @param {String} [options.password]  A WP-API Basic Auth password
+ * @param {String} [options.nonce]     A WP nonce for use with cookie authentication
+ * @param {Object} [options.routes]    A dictionary of API routes with which to
+ *                                     bootstrap the WPAPI instance: the instance will
+ *                                     be initialized with default routes only
+ *                                     if this property is omitted
+ * @param {String} [options.transport] An optional dictionary of HTTP transport
+ *                                     methods (.get, .post, .put, .delete, .head)
+ *                                     to use instead of the defaults, e.g. to use
+ *                                     a different HTTP library than superagent
+ */
+function WPAPI( options ) {
+
+	// Enforce `new`
+	if ( this instanceof WPAPI === false ) {
+		return new WPAPI( options );
+	}
+
+	if ( typeof options.endpoint !== 'string' ) {
+		throw new Error( 'options hash must contain an API endpoint URL string' );
+	}
+
+	// Dictionary to be filled by handlers for default namespaces
+	this._ns = {};
+
+	this._options = {
+		// Ensure trailing slash on endpoint URI
+		endpoint: options.endpoint.replace(  /\/?$/, '/' )
+	};
+
+	// If any authentication credentials were provided, assign them now
+	if ( options && ( options.username || options.password || options.nonce ) ) {
+		this.auth( options );
+	}
+
+	return this
+		// Configure custom HTTP transport methods, if provided
+		.transport( options.transport )
+		// Bootstrap with a specific routes object, if provided
+		.bootstrap( options && options.routes );
+}
+
+/**
+ * Set custom transport methods to use when making HTTP requests against the API
+ *
+ * Pass an object with a function for one or many of "get", "post", "put",
+ * "delete" and "head" and that function will be called when making that type
+ * of request. The provided transport functions should take a WPRequest handler
+ * instance (_e.g._ the result of a `wp.posts()...` chain or any other chaining
+ * request handler) as their first argument; a `data` object as their second
+ * argument (for POST, PUT and DELETE requests); and an optional callback as
+ * their final argument. Transport methods should invoke the callback with the
+ * response data (or error, as appropriate), and should also return a Promise.
+ *
+ * @example <caption>showing how a cache hit (keyed by URI) could short-circuit a get request</caption>
+ *
+ *     var site = new WPAPI({
+ *       endpoint: 'http://my-site.com/wp-json'
+ *     });
+ *
+ *     // Overwrite the GET behavior to inject a caching layer
+ *     site.transport({
+ *       get: function( wpreq, cb ) {
+ *         var result = cache[ wpreq ];
+ *         // If a cache hit is found, return it via the same callback/promise
+ *         // signature as the default transport method
+ *         if ( result ) {
+ *           if ( cb && typeof cb === 'function' ) {
+ *             cb( null, result );
+ *           }
+ *           return Promise.resolve( result );
+ *         }
+ *
+ *         // Delegate to default transport if no cached data was found
+ *         return WPAPI.transport.get( wpreq, cb ).then(function( result ) {
+ *           cache[ wpreq ] = result;
+ *           return result;
+ *         });
+ *       }
+ *     });
+ *
+ * This is advanced behavior; you will only need to utilize this functionality
+ * if your application has very specific HTTP handling or caching requirements.
+ * Refer to the "http-transport" module within this application for the code
+ * implementing the built-in transport methods.
+ *
+ * @memberof! WPAPI
+ * @method transport
+ * @chainable
+ * @param {Object}   transport          A dictionary of HTTP transport methods
+ * @param {Function} [transport.get]    The function to use for GET requests
+ * @param {Function} [transport.post]   The function to use for POST requests
+ * @param {Function} [transport.put]    The function to use for PUT requests
+ * @param {Function} [transport.delete] The function to use for DELETE requests
+ * @param {Function} [transport.head]   The function to use for HEAD requests
+ * @returns {WPAPI} The WPAPI instance, for chaining
+ */
+WPAPI.prototype.transport = function( transport ) {
+	// Local reference to avoid need to reference via `this` inside forEach
+	var _options = this._options;
+
+	// Create the default transport if it does not exist
+	if ( ! _options.transport ) {
+		_options.transport = Object.create( WPAPI.transport );
+	}
+
+	// Whitelist the methods that may be applied
+	[ 'get', 'head', 'post', 'put', 'delete' ].forEach(function( key ) {
+		if ( transport && transport[ key ] ) {
+			_options.transport[ key ] = transport[ key ];
+		}
+	});
+
+	return this;
+};
+
+/**
+ * Default HTTP transport methods object for all WPAPI instances
+ *
+ * These methods may be extended or replaced on an instance-by-instance basis
+ *
+ * @memberof! WPAPI
+ * @static
+ * @property transport
+ * @type {Object}
+ */
+WPAPI.transport = Object.create( httpTransport );
+Object.freeze( WPAPI.transport );
+
+/**
+ * Convenience method for making a new WPAPI instance
+ *
+ * @example
+ * These are equivalent:
+ *
+ *     var wp = new WPAPI({ endpoint: 'http://my.blog.url/wp-json' });
+ *     var wp = WPAPI.site( 'http://my.blog.url/wp-json' );
+ *
+ * `WPAPI.site` can take an optional API root response JSON object to use when
+ * bootstrapping the client's endpoint handler methods: if no second parameter
+ * is provided, the client instance is assumed to be using the default API
+ * with no additional plugins and is initialized with handlers for only those
+ * default API routes.
+ *
+ * @example
+ * These are equivalent:
+ *
+ *     // {...} means the JSON output of http://my.blog.url/wp-json
+ *     var wp = new WPAPI({
+ *       endpoint: 'http://my.blog.url/wp-json',
+ *       json: {...}
+ *     });
+ *     var wp = WPAPI.site( 'http://my.blog.url/wp-json', {...} );
+ *
+ * @memberof! WPAPI
+ * @static
+ * @param {String} endpoint The URI for a WP-API endpoint
+ * @param {Object} routes   The "routes" object from the JSON object returned
+ *                          from the root API endpoint of a WP site, which should
+ *                          be a dictionary of route definition objects keyed by
+ *                          the route's regex pattern
+ * @returns {WPAPI} A new WPAPI instance, bound to the provided endpoint
+ */
+WPAPI.site = function( endpoint, routes ) {
+	return new WPAPI({
+		endpoint: endpoint,
+		routes: routes
+	});
+};
+
+/**
+ * Generate a request against a completely arbitrary endpoint, with no assumptions about
+ * or mutation of path, filtering, or query parameters. This request is not restricted to
+ * the endpoint specified during WPAPI object instantiation.
+ *
+ * @example
+ * Generate a request to the explicit URL "http://your.website.com/wp-json/some/custom/path"
+ *
+ *     wp.url( 'http://your.website.com/wp-json/some/custom/path' ).get()...
+ *
+ * @memberof! WPAPI
+ * @param {String} url The URL to request
+ * @returns {WPRequest} A WPRequest object bound to the provided URL
+ */
+WPAPI.prototype.url = function( url ) {
+	var options = extend( {}, this._options, {
+		endpoint: url
+	});
+	return new WPRequest( options );
+};
+
+/**
+ * Generate a query against an arbitrary path on the current endpoint. This is useful for
+ * requesting resources at custom WP-API endpoints, such as WooCommerce's `/products`.
+ *
+ * @memberof! WPAPI
+ * @param {String} [relativePath] An endpoint-relative path to which to bind the request
+ * @returns {WPRequest} A request object
+ */
+WPAPI.prototype.root = function( relativePath ) {
+	relativePath = relativePath || '';
+	var options = extend( {}, this._options );
+	// Request should be
+	var request = new WPRequest( options );
+
+	// Set the path template to the string passed in
+	request._path = { '0': relativePath };
+
+	return request;
+};
+
+/**
+ * Set the default headers to use for all HTTP requests created from this WPAPI
+ * site instance. Accepts a header name and its associated value as two strings,
+ * or multiple headers as an object of name-value pairs.
+ *
+ * @example <caption>Set a single header to be used by all requests to this site</caption>
+ *
+ *     site.setHeaders( 'Authorization', 'Bearer trustme' )...
+ *
+ * @example <caption>Set multiple headers to be used by all requests to this site</caption>
+ *
+ *     site.setHeaders({
+ *       Authorization: 'Bearer comeonwereoldfriendsright',
+ *       'Accept-Language': 'en-CA'
+ *     })...
+ *
+ * @memberof! WPAPI
+ * @since 1.1.0
+ * @chainable
+ * @param {String|Object} headers The name of the header to set, or an object of
+ *                                header names and their associated string values
+ * @param {String}        [value] The value of the header being set
+ * @returns {WPAPI} The WPAPI site handler instance, for chaining
+ */
+WPAPI.prototype.setHeaders = WPRequest.prototype.setHeaders;
+
+/**
+ * Set the authentication to use for a WPAPI site handler instance. Accepts basic
+ * HTTP authentication credentials (string username & password) or a Nonce (for
+ * cookie authentication) by default; may be overloaded to accept OAuth credentials
+ * in the future.
+ *
+ * @example <caption>Basic Authentication</caption>
+ *
+ *     site.auth({
+ *       username: 'admin',
+ *       password: 'securepass55'
+ *     })...
+ *
+ * @example <caption>Cookie/Nonce Authentication</caption>
+ *
+ *     site.auth({
+ *       nonce: 'somenonce'
+ *     })...
+ *
+ * @memberof! WPAPI
+ * @method
+ * @chainable
+ * @param {Object} credentials            An authentication credentials object
+ * @param {String} [credentials.username] A WP-API Basic HTTP Authentication username
+ * @param {String} [credentials.password] A WP-API Basic HTTP Authentication password
+ * @param {String} [credentials.nonce]    A WP nonce for use with cookie authentication
+ * @returns {WPAPI} The WPAPI site handler instance, for chaining
+ */
+WPAPI.prototype.auth = WPRequest.prototype.auth;
+
+// Apply the registerRoute method to the prototype
+WPAPI.prototype.registerRoute = require( './lib/wp-register-route' );
+
+/**
+ * Deduce request methods from a provided API root JSON response object's
+ * routes dictionary, and assign those methods to the current instance. If
+ * no routes dictionary is provided then the instance will be bootstrapped
+ * with route handlers for the default API endpoints only.
+ *
+ * This method is called automatically during WPAPI instance creation.
+ *
+ * @memberof! WPAPI
+ * @chainable
+ * @param {Object} routes The "routes" object from the JSON object returned
+ *                        from the root API endpoint of a WP site, which should
+ *                        be a dictionary of route definition objects keyed by
+ *                        the route's regex pattern
+ * @returns {WPAPI} The bootstrapped WPAPI client instance (for chaining or assignment)
+ */
+WPAPI.prototype.bootstrap = function( routes ) {
+	var routesByNamespace;
+	var endpointFactoriesByNamespace;
+
+	if ( ! routes ) {
+		// Auto-generate default endpoint factories if they are not already available
+		if ( ! defaultEndpointFactories ) {
+			routesByNamespace = buildRouteTree( defaultRoutes );
+			defaultEndpointFactories = generateEndpointFactories( routesByNamespace );
+		}
+		endpointFactoriesByNamespace = defaultEndpointFactories;
+	} else {
+		routesByNamespace = buildRouteTree( routes );
+		endpointFactoriesByNamespace = generateEndpointFactories( routesByNamespace );
+	}
+
+	// For each namespace for which routes were identified, store the generated
+	// route handlers on the WPAPI instance's private _ns dictionary. These namespaced
+	// handler methods can be accessed by calling `.namespace( str )` on the
+	// client instance and passing a registered namespace string.
+	// Handlers for default (wp/v2) routes will also be assigned to the WPAPI
+	// client instance object itself, for brevity.
+	return objectReduce( endpointFactoriesByNamespace, function( wpInstance, endpointFactories, namespace ) {
+
+		// Set (or augment) the route handler factories for this namespace.
+		wpInstance._ns[ namespace ] = objectReduce( endpointFactories, function( nsHandlers, handlerFn, methodName ) {
+			nsHandlers[ methodName ] = handlerFn;
+			return nsHandlers;
+		}, wpInstance._ns[ namespace ] || {
+			// Create all namespace dictionaries with a direct reference to the main WPAPI
+			// instance's _options property so that things like auth propagate properly
+			_options: wpInstance._options
+		} );
+
+		// For the default namespace, e.g. "wp/v2" at the time this comment was
+		// written, ensure all methods are assigned to the root client object itself
+		// in addition to the private _ns dictionary: this is done so that these
+		// methods can be called with e.g. `wp.posts()` and not the more verbose
+		// `wp.namespace( 'wp/v2' ).posts()`.
+		if ( namespace === apiDefaultNamespace ) {
+			Object.keys( wpInstance._ns[ namespace ] ).forEach(function( methodName ) {
+				wpInstance[ methodName ] = wpInstance._ns[ namespace ][ methodName ];
+			});
+		}
+
+		return wpInstance;
+	}, this );
+};
+
+/**
+ * Access API endpoint handlers from a particular API namespace object
+ *
+ * @example
+ *
+ *     wp.namespace( 'myplugin/v1' ).author()...
+ *
+ *     // Default WP endpoint handlers are assigned to the wp instance itself.
+ *     // These are equivalent:
+ *     wp.namespace( 'wp/v2' ).posts()...
+ *     wp.posts()...
+ *
+ * @memberof! WPAPI
+ * @param {string} namespace A namespace string
+ * @returns {Object} An object of route endpoint handler methods for the
+ * routes within the specified namespace
+ */
+WPAPI.prototype.namespace = function( namespace ) {
+	if ( ! this._ns[ namespace ] ) {
+		throw new Error( 'Error: namespace ' + namespace + ' is not recognized' );
+	}
+	return this._ns[ namespace ];
+};
+
+/**
+ * Take an arbitrary WordPress site, deduce the WP REST API root endpoint, query
+ * that endpoint, and parse the response JSON. Use the returned JSON response
+ * to instantiate a WPAPI instance bound to the provided site.
+ *
+ * @memberof! WPAPI
+ * @static
+ * @param {string} url A URL within a REST API-enabled WordPress website
+ * @returns {Promise} A promise that resolves to a configured WPAPI instance bound
+ * to the deduced endpoint, or rejected if an endpoint is not found or the
+ * library is unable to parse the provided endpoint.
+ */
+WPAPI.discover = function( url ) {
+	// local placeholder for API root URL
+	var endpoint;
+
+	// Try HEAD request first, for smaller payload: use WPAPI.site to produce
+	// a request that utilizes the defined HTTP transports
+	var req = WPAPI.site( url ).root();
+	return req.headers()
+		.catch(function() {
+			// On the hypothesis that any error here is related to the HEAD request
+			// failing, provisionally try again using GET because that method is
+			// more widely supported
+			return req.get();
+		})
+		// Inspect response to find API location header
+		.then( autodiscovery.locateAPIRootHeader )
+		.then(function( apiRootURL ) {
+			// Set the function-scope variable that will be used to instantiate
+			// the bound WPAPI instance,
+			endpoint = apiRootURL;
+
+			// then GET the API root JSON object
+			return WPAPI.site( apiRootURL ).root().get();
+		})
+		.then(function( apiRootJSON ) {
+			// Instantiate & bootstrap with the discovered methods
+			return new WPAPI({
+				endpoint: endpoint,
+				routes: apiRootJSON.routes
+			});
+		})
+		.catch(function( err ) {
+			console.error( err );
+			if ( endpoint ) {
+				console.warn( 'Endpoint detected, proceeding despite error...' );
+				console.warn( 'Binding to ' + endpoint + ' and assuming default routes' );
+				return new WPAPI.site( endpoint );
+			}
+			throw new Error( 'Autodiscovery failed' );
+		});
+};
+
+module.exports = WPAPI;
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.4.3 on Thu Jul 06 2017 14:09:15 GMT-0400 (EDT) using the Minami theme. +
+ + + + + diff --git a/bin/jekyll.js b/bin/jekyll.js deleted file mode 100755 index 4b94ed08..00000000 --- a/bin/jekyll.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * This script will start the Jekyll server in the context of the docs - * directory. It is only for use in local development, and sets the --baseurl - * option to override the production-only baseurl in _config.yml. - */ -/* eslint-disable no-console */ -'use strict'; - -const path = require( 'path' ); -const spawn = require( 'child_process' ).spawn; -const argv = require( 'minimist' )( process.argv.slice( 2 ) ); - -// Execute within the context of the docs directory -const docsDir = path.resolve( __dirname, '../documentation' ); - -if ( argv.install || argv.i ) { - // Install the ruby bundle needed to run jekyll - const server = spawn( 'bundle', [ 'install' ], { - cwd: docsDir, - stdio: 'inherit', - } ); - - server.on( 'error', err => console.error( err ) ); -} else { - // Start the server in local dev mode - const bundleOptions = [ 'exec', 'jekyll', 'serve', '--baseurl', '' ]; - if ( argv.host ) { - bundleOptions.push( '--host', argv.host ); - } - - const server = spawn( 'bundle', bundleOptions, { - cwd: docsDir, - stdio: 'inherit', - } ); - - server.on( 'error', err => console.error( err ) ); -} diff --git a/build/.eslintrc.js b/build/.eslintrc.js deleted file mode 100644 index 7214a57c..00000000 --- a/build/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - 'rules': { - 'no-console': [ 'off' ], - }, -}; diff --git a/build/api-reference-readme.md b/build/api-reference-readme.md deleted file mode 100644 index 51fc1d0c..00000000 --- a/build/api-reference-readme.md +++ /dev/null @@ -1,7 +0,0 @@ -## WPAPI Code Reference - -Welcome to the API Reference for the `wpapi` NPM package. - -Running `require( 'wpapi' )` returns the `WPAPI` constructor, which you can read about by clicking its name in the class list in the menu. Each request handler factory on `WPAPI` (such as `.posts()`, `.pages()`, _etc._) returns a `WPRequest` object, conditionally augmented with one or more mixins depending on the capabilities of the associated endpoints. - -For user guides & tutorials, visit [wp-api.org/node-wpapi/](http://wp-api.org/node-wpapi). diff --git a/build/grunt/clean.js b/build/grunt/clean.js deleted file mode 100644 index 5b1cad14..00000000 --- a/build/grunt/clean.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -module.exports = function( grunt ) { - grunt.config.set( 'clean', { - generated_api_docs: [ 'documentation/api-reference' ], - generated_pages: [ 'documentation/*.md' ], - generated_files: [ 'documentation/index.html', 'documentation/404.html' ], - generated_zip: [ 'documentation/*.zip' ], - leftover_pages_from_docs_branch: [ - './Gemfile.lock', - './_pages/', - './_site/', - './api-reference/', - './css/', - './index.html.combyne', - ], - } ); - - grunt.loadNpmTasks( 'grunt-contrib-clean' ); -}; diff --git a/build/grunt/generate-docs.js b/build/grunt/generate-docs.js deleted file mode 100644 index 87cf2873..00000000 --- a/build/grunt/generate-docs.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -module.exports = function( grunt ) { - grunt.registerTask( 'generate_readme_docs', [ - 'Parse the contents of README.md out into individual markdown pages that', - 'can be rendered with Jekyll.', - ].join( ' ' ), function() { - // Force task into async mode and grab a handle to the "done" function. - const done = this.async(); - - grunt.log.writeln( 'Extracting page content from README.md...' ); - - // Kick off generation - require( '../scripts/generate-docs-markdown' ).then( () => { - grunt.log.writeln( 'Pages generated successfully' ); - done(); - } ); - } ); -}; diff --git a/build/grunt/zip.js b/build/grunt/zip.js deleted file mode 100644 index b5950b2b..00000000 --- a/build/grunt/zip.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = function( grunt ) { - grunt.config.set( 'zip', { - bundle: { - cwd: 'browser', - src: [ 'browser/**/*', 'LICENSE' ], - dest: 'documentation/wpapi.zip', - }, - } ); - - grunt.loadNpmTasks( 'grunt-zip' ); -}; diff --git a/build/scripts/generate-docs-markdown.js b/build/scripts/generate-docs-markdown.js deleted file mode 100644 index 8dfa808a..00000000 --- a/build/scripts/generate-docs-markdown.js +++ /dev/null @@ -1,242 +0,0 @@ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const kramed = require( 'kramed' ); -const combyne = require( 'combyne' ); -combyne.settings.delimiters = { - // Avoid conflict with Jekyll template delimiters - START_RAW: '[{{{', - END_RAW: '}}}]', - START_PROP: '[{{', - END_PROP: '}}]', - START_EXPR: '[{%', - END_EXPR: '%}]', -}; - -// Application Version -const version = require( '../../package.json' ).version; - -// Paths -const projectRoot = path.join( __dirname, '../..' ); -const docsDir = path.join( projectRoot, 'documentation' ); -const readmePath = path.join( projectRoot, 'README.md' ); -const changelogPath = path.join( projectRoot, 'CHANGELOG.md' ); -const contributingPath = path.join( projectRoot, 'CONTRIBUTING.md' ); -const licensePath = path.join( projectRoot, 'LICENSE' ); -const indexTemplatePath = path.join( projectRoot, 'documentation', 'index.html.combyne' ); -const err404TemplatePath = path.join( projectRoot, 'documentation', '404.html.combyne' ); - -// This constant defines the minimum importance of header for which files will -// be created: e.g. if this is 2, files will only be created for headers # & ## -const README_SPLIT_LEVEL = 2; - -// This is a list of slugs to skip when rendering the page index on the homepage -const SKIP_SECTION_LINKS = [ - // "About" content is embedded into the index page - 'about', - // API Documentation provided via YUIDoc & the link is injected into the index - 'api-documentation', -]; - -// This is a list of slugs to skip when generating pages from README sections -const SKIP_SECTIONS = SKIP_SECTION_LINKS.concat( [ - // CONTRIBUTING.md supersedes the README's contributing section - 'contributing', -] ); - -const pad = ( num, digits ) => { - let str = '' + parseInt( num, 10 ); - while ( str.length < digits ) { - str = '0' + str; - } - return str; -}; - -const titleToSlug = title => title - .toLowerCase() - .replace( /[^\w]/g, ' ' ) - .trim() - .split( /\s+/ ) - .join( '-' ); - -const fileHeader = ( title ) => { - const slug = titleToSlug( title ); - return `---\nlayout: page\ntitle: ${ title }\npermalink: /${ slug }/\n---`; -}; - -const titleRE = /^\n+#+([^\n]+)\n+$/; -const hasSectionsRE = /\n#+([^\n]+)\n/; - -const isTitle = token => token.match( titleRE ); - -const getTitle = token => token.replace( titleRE, '$1' ).trim(); - -const getLevel = ( mdHeading ) => { - const match = mdHeading.match( /#+/ ); - return match ? match[ 0 ].length : -1; -}; - -const getContents = ( entry ) => { - // Strip any top-level headings: those are rendered as titles elsewhere - const fileContents = entry.tokens.join( '' ).replace( /^# [^\n]+/, '' ); - const title = fileHeader( entry.title ); - - // Only include the ToC placeholder if a ToC is needed - return hasSectionsRE.test( fileContents ) ? - [ title, '* TOC\n{:toc}', fileContents ].join( '\n\n' ) : - [ title, fileContents ].join( '\n\n' ); -}; - -// Promise-based File System helpers -const readFile = sourcePath => new Promise( ( resolve, reject ) => { - fs.readFile( sourcePath, ( err, contents ) => { - if ( err ) { - return reject( err ); - } - - // contents is a Buffer - resolve( contents.toString() ); - } ); -} ); - -const writeFile = ( outputPath, fileContents ) => new Promise( ( resolve, reject ) => { - fs.writeFile( outputPath, fileContents, ( err ) => { - if ( err ) { - return reject( err ); - } - - resolve(); - } ); -} ); - -const copyFile = ( sourcePath, title ) => readFile( sourcePath ).then( ( contents ) => { - const outputPath = path.join( docsDir, `${ titleToSlug( title ) }.md` ); - return writeFile( outputPath, getContents( { - title: title, - tokens: [ contents ], - } ) ); -} ); - -// Break the README into individual files -const readmeOutput = readFile( readmePath ).then( ( contents ) => { - const tokens = contents.split( /(\n+#+ .*\n+)/ ); - const entries = []; - let entry = null; - - for ( let i = 0; i < tokens.length; i++ ) { - const token = tokens[ i ]; - - if ( ! isTitle( token ) ) { - if ( entry && entry.tokens ) { - entry.tokens.push( token ); - } - continue; - } - - const level = getLevel( token ); - - if ( level > README_SPLIT_LEVEL ) { - entry.tokens.push( token ); - - if ( level === README_SPLIT_LEVEL + 1 ) { - entry.subheadings.push( { - slug: `${ entry.slug }#${ titleToSlug( token ) }`, - title: getTitle( token ), - level: level, - } ); - } - continue; - } - - entry = { - heading: token, - slug: titleToSlug( token ), - title: getTitle( token ), - level: level, - subheadings: [], - tokens: [], - }; - - entries.push( entry ); - } - - entries.forEach( entry => entry.contents = getContents( entry ) ); - - return entries.reduce( ( previous, entry, idx ) => { - return previous.then( () => { - const outputPath = path.join( docsDir, `${ pad( idx, 2 ) }-${ entry.slug }.md` ); - - return SKIP_SECTIONS.indexOf( entry.slug ) === -1 ? - writeFile( outputPath, entry.contents ) : - Promise.resolve(); - } ); - }, Promise.resolve() ).then( () => { - entries.push( { - title: 'API Documentation', - slug: `api-reference/wpapi/${version}/`, - } ); - return entries; - } ); -} ); - -// Create the contributor guide (runs after the README files are processed in -// order to overwrite the "contributing" README section, if present) -const contributingOutput = readmeOutput.then( () => copyFile( contributingPath, 'Contributing' ) ); - -// Create the changelog page -const changelogOutput = copyFile( changelogPath, 'Changelog' ); - -// Create the license page -const licenseOutput = copyFile( licensePath, 'License' ); - -// Build the template context to use with the -const templateContext = readmeOutput.then( ( entries ) => { - return entries.reduce( ( context, entry ) => { - const isAboutPage = entry.slug === 'about'; - if ( isAboutPage ) { - context.aboutContents = kramed( entry.tokens.join( '' ) ); - } else if ( SKIP_SECTION_LINKS.indexOf( entry.slug ) === -1 ) { - entry.hasSubheadings = entry.subheadings && entry.subheadings.length; - context.readmeSections.push( entry ); - } - return context; - }, { - aboutContents: null, - readmeSections: [], - } ); -} ); - -const fileAndContext = filePath => Promise.all( [ - readFile( filePath ), - templateContext, -] ).then( result => ( { - template: result[ 0 ], - context: result[ 1 ], -} ) ); - -// Create the index HTML page -const indexOutput = fileAndContext( indexTemplatePath ).then( ( result ) => { - console.log( 'index' ); - const outputPath = path.join( docsDir, 'index.html' ); - const fileContents = combyne( result.template ).render( result.context ); - return writeFile( outputPath, fileContents ); -} ); - -// Create the Error 404 page -const err404Output = fileAndContext( err404TemplatePath ).then( ( result ) => { - console.log( '404' ); - const outputPath = path.join( docsDir, '404.html' ); - const fileContents = combyne( result.template ).render( result.context ); - return writeFile( outputPath, fileContents ); -} ); - -module.exports = Promise.all( [ - readmeOutput, - contributingOutput, - changelogOutput, - licenseOutput, - indexOutput, - err404Output, -] ) - .catch( err => console.log( err && err.stack ) ); diff --git a/build/scripts/release-docs.js b/build/scripts/release-docs.js deleted file mode 100644 index ab39ad37..00000000 --- a/build/scripts/release-docs.js +++ /dev/null @@ -1,239 +0,0 @@ -'use strict'; - -const fs = require( 'fs' ); -const path = require( 'path' ); -const prompt = require( 'prompt' ); -const spawn = require( 'child_process' ).spawn; - -// Get package info -const pkg = require( '../../package.json' ); - -// This is the commit message that will be used by the script -const commitMessage = `Release latest documentation for node-wpapi v${pkg.version}`; - -// Run all commands in base project directory -const projectRoot = path.join( __dirname, '../..' ); - -// This regex is used to exclude hidden files, the Gemfile.lock, and combyne -// templates (which are used to generate Jekyll files, and not needed on the -// deployed site) -const OMIT_FILE_RE = /^\.|\.lock|\.combyne$/; - -// This regex is used to find directories to explicitly rm -rf when copying -// files from the temporary directory into the repo proper: we make the -// incorrect but still helpful assumption that no file extension means that -// something is a directory. This means Gemfile and other no-ext files are -// unnecessarily deleted, but it makes things work with minimal complexity. -const PROBABLY_A_DIRECTORY_RE = /^[^.]+$/; - -// RE to match the markdown files that are extracted from the README.md -const GENERATED_MARKDOWN_RE = /^\d+-.*\.md$/; - -// RE to match "y", yes", "yeah", "yes please", and other affirmative responses -const AFFIRMATIVE_RE = /^y(:?e[asp]?h?)?(:? [^\n]+)?\s*$/i; - -/** - * Helper method to request yes/no confirmation from the user, with some - * - * @private - * @returns {Promise} A promise that will resolve with a boolean true/false - * indicating whether assent was given - */ -const promptYN = () => new Promise( ( resolve, reject ) => { - prompt.message = ''; - prompt.start(); - prompt.get( [ 'y/n' ], ( err, result ) => { - if ( err ) { - return reject( err ); - } - resolve( AFFIRMATIVE_RE.test( result[ 'y/n' ] ) ); - } ); -} ); - -/** - * Get the list of files in a directory, either as a list of file and subdir - * names or a list of absolute file system paths - * - * @private - * @param {string} inputDir The file system path to the directory to read - * @returns {Promise} A promise to the string array of file names - */ -const ls = ( inputDir ) => { - return new Promise( ( resolve, reject ) => { - fs.readdir( inputDir, ( err, list ) => { - if ( err ) { - return reject( err ); - } - - resolve( list ); - } ); - } ); -}; - -/** - * Spawn a shell command, pipe its output to the parent process's stdio, and - * return a promise that will resolve or reject when the subprocess completes - * - * @private - * @param {string} commandStr A string containing a shell command to run - * @param {string[]} [otherArgs] Array of additional string arguments, so that - * quoted arguments with spaces like github commit messages will not be broken - * @returns {Promise} A promise that will resolve if the command executes - * successfully or reject if it errors - */ -const runCommand = ( commandStr, otherArgs ) => { - return new Promise( ( resolve, reject ) => { - const commandArgs = commandStr.split( ' ' ); - if ( Array.isArray( otherArgs ) ) { - otherArgs.forEach( arg => commandArgs.push( arg ) ); - } - const command = commandArgs.shift(); - - const spawnedCommand = spawn( command, commandArgs, { - cwd: projectRoot, - stdio: 'inherit', - } ); - - spawnedCommand.on( 'error', ( err ) => { - reject( err ); - } ); - - spawnedCommand.on( 'close', ( code ) => { - return code ? reject( code ) : resolve(); - } ); - } ); -}; - -/** - * Helper function that takes in an array of functions that return promises, - * then executes those functions sequentially to execute each action - * - * @param {function[]} arrOfFnsReturningPromises An array of functions - * @returns {Promise} A promise that will resolve once all the promises - * returned by that function successfully complete - */ -const runInSequence = ( arrOfFnsReturningPromises ) => { - return arrOfFnsReturningPromises.reduce( - ( lastStep, startNextStep ) => lastStep.then( startNextStep ), - Promise.resolve() - ); -}; - -// Start fresh -runCommand( 'rm -rf docs-tmp' ) - // Make sure that we know what we are doing - .then( () => new Promise( ( resolve, reject ) => { - console.log( '\nBefore we begin,' ); - console.log( '1. Are you on the "master" branch?' ); - console.log( '2. Does "git status" show that HEAD is clean?' ); - console.log( '3. Is the code in "master" current with a released version?' ); - console.log( '\nWhatever is in this branch will be released in the public .zip download.' ); - console.log( 'By proceeding, you affirm that this is not going to ruin anybody\'s day.' ); - console.log( '\nContinue?' ); - - return promptYN().then( ( result ) => { - if ( result ) { - console.log( '\nGreat, let\'s get this show on the road...' ); - resolve(); - } else { - console.log( '\nDiscretion is the better part of valor\n' ); - // Throw a raw string to make the logging easier - reject( '(User aborted deployment process)' ); - } - } ); - } ) ) - // Ensure the built JS library is up to date - .then( () => runCommand( 'npm run build' ) ) - // Build the docs site content (web bundle .zip, generated pages, etc) - .then( () => runCommand( 'npm run docs' ) ) - // Copy the docs folder to a temp location to move its contents across branches - .then( () => runCommand( 'cp -r documentation docs-tmp' ) ) - .then( () => console.log( '\nTemporary directory created successfully. Switching branches...\n' ) ) - // Switch to the docs site branch - .then( () => runCommand( 'git checkout gh-pages' ) ) - // Remove auto-generated files from the root of the gh-pages branch, in case - // file names have changed since the last deploy - .then( () => ls( projectRoot ) - .then( ( files ) => { - const removeFiles = files - .filter( file => GENERATED_MARKDOWN_RE.test( file ) ) - .map( file => () => runCommand( `rm ${file}` ) ); - - return runInSequence( removeFiles ); - } ) - ) - .then( () => console.log( '\nCopying files from temp directory...\n' ) ) - // Get a list of generated files in the temp directory - .then( () => ls( path.join( projectRoot, 'docs-tmp' ) ) ) - // Filter out unneeded files from the list - .then( fileList => fileList.filter( result => ! OMIT_FILE_RE.test( result ) ) ) - // Copy things from the temp directory down into the directory root - .then( ( fileList ) => { - // Create an array of functions that each remove a directory in the root of - // this project which could block the success of the `mv` command below - const removeDirectories = fileList - .filter( file => PROBABLY_A_DIRECTORY_RE.test( file ) ) - .map( ( dir ) => { - // Ignore errors b/c they will usually be nothing more than a warning - // that a file we tried to delete didn't exist to begin with - return () => runCommand( `rm -rf ${dir}` ).catch( err => console.log( err ) ); - } ); - - return runInSequence( removeDirectories ).then( () => fileList ); - } ) - // Copy files over - .then( ( fileList ) => { - const copyFiles = fileList.map( ( file ) => { - return () => runCommand( `mv docs-tmp/${file} ./${file}` ); - } ); - return runInSequence( copyFiles ).then( () => fileList ); - } ) - .then( fileList => console.log( `${fileList.length} files moved successfully` ) ) - // Remove the temp directory - .then( () => runCommand( 'rm -rf docs-tmp' ) ) - // Give the option of not re-deploying wpapi.zip: if the package's contents - // have not changed but a new .zip is generated with the same contents, Git - // will still regard it as an updated file and too many of those could bloat - // the repo. Easier to exclude it for docs-only updates. - .then( () => new Promise( ( resolve ) => { - console.log( '\nHas the wpapi.zip bundle changed since last deploy? (If unsure, answer "Yes")' ); - - return promptYN().then( ( result ) => { - if ( result ) { - console.log( 'Including updated wpapi.zip in build...' ); - resolve(); - } else { - console.log( 'Removing unneeded wpapi.zip update from the commit' ); - resolve( runCommand( 'git checkout gh-pages wpapi.zip' ) ); - } - } ); - } ) ) - // Stage files for commit - .then( () => runCommand( 'git add .' ) ) - // Require user confirmation before proceeding with the commit & push - .then( () => new Promise( ( resolve, reject ) => { - console.log( '\nDocumentation staged for commit. Proceed with commit & push?' ); - - return promptYN().then( ( result ) => { - if ( result ) { - console.log( '\nConfirmed, committing & pushing docs branch...' ); - resolve(); - } else { - console.log( '\nCommit & push canceled, exiting.\n' ); - // Throw a raw string to make the logging easier - reject( '(User aborted deployment process)' ); - } - } ); - } ) ) - // Commit files - .then( () => runCommand( 'git commit -m', [ `"${commitMessage}"` ] ) ) - // Push docs branch - .then( () => runCommand( 'git push origin gh-pages' ) ) - // Switch back to master - .then( () => runCommand( 'git checkout master' ) ) - // Run the clean command again to remove any ignored docs files left over - // after switching back to master - .then( () => runCommand( 'grunt clean' ) ) - // Log success! - .then( () => console.log( '\nDocs site updated!' ) ) - .catch( err => console.error( err ) ); diff --git a/build/scripts/simplify-object.js b/build/scripts/simplify-object.js deleted file mode 100644 index d9a99a52..00000000 --- a/build/scripts/simplify-object.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Helper method used by the "update default routes JSON"" script - */ -'use strict'; - -const objectReduce = require( '../../lib/util/object-reduce' ); - -/** - * Walk through the keys and values of a provided object, removing any properties - * which would be inessential to the generation of the route tree used to deduce - * route handlers from a `wp-json/` root API endpoint. This module is not used by - * the wpapi module itself, but is rather a dependency of the script that is used - * to create the `endpoint-response.json` file that is shipped along with this - * module for use in generating the "default" routes. - * - * @param {*} obj An arbitrary JS value, probably an object - * @returns {*} The passed-in value, with non-essential args properties and all - * _links properties removes. - */ -const simplifyObject = ( obj ) => { - // Pass through falsy values, Dates and RegExp values without modification - if ( ! obj || obj instanceof Date || obj instanceof RegExp ) { - return obj; - } - - if ( obj.methods && obj.args ) { - // If the key is an object with "methods" and "args" properties, only - // include the full "args" object if "methods" contains GET. - if ( ! obj.methods.map( str => str.toLowerCase() ).includes( 'get' ) ) { - obj.args = {}; - } - } - - // Map arrays through simplifyObject - if ( Array.isArray( obj ) ) { - return obj.map( simplifyObject ); - } - - // Reduce through objects to run each property through simplifyObject - if ( typeof obj === 'object' ) { - return objectReduce( - obj, - ( newObj, val, key ) => { - // Omit _links objects entirely - if ( key === '_links' ) { - return newObj; - } - - // If the key is "args", omit all keys of second-level descendants - if ( key === 'args' ) { - newObj.args = objectReduce( - val, - ( slimArgs, argVal, argKey ) => { - slimArgs[ argKey ] = {}; - return slimArgs; - }, - {} - ); - } else { - // Pass all other objects through simplifyObject - newObj[ key ] = simplifyObject( obj[ key ] ); - } - return newObj; - }, - {} - ); - } - - // All other types pass through without modification - return obj; -}; - -module.exports = simplifyObject; diff --git a/build/scripts/update-default-routes-json.js b/build/scripts/update-default-routes-json.js deleted file mode 100755 index 1cbcd47f..00000000 --- a/build/scripts/update-default-routes-json.js +++ /dev/null @@ -1,169 +0,0 @@ -/** - * To avoid requiring that auto-discovery be utilized every time the API client - * is initialized, this library ships with a built-in route definition from a - * vanilla WordPress REST API installation. That file may be updated by - * installing the API plugin on a clean WP development instance, with no other - * plugins running, and downloading the JSON output from `yourwpsite.com/wp-json/` - * into the "default-routes.json" file in this directory. - * - * That file can also be generated by running this script against the same live - * WP REST API instance to download that same file, the difference being that, - * if the `default-routes.json` file is downloaded through this script, it - * will be run through the `simplifyObject` utility to cut out about 1/3 of the - * bytes of the response by removing properties that do not effect route generation. - * - * This script is NOT intended to be a dependency of any part of wp.js, and is - * provided purely as a utility for upgrading the built-in copy of the endpoint - * response JSON file that is used to bootstrap the default route handlers. - * - * @example - * - * # Invoke directly, run against default endpoint (details below) - * ./update-default-routes-json.js - * - * # Invoke with `node` CLI, and run against a custom endpoint - * node ./update-default-routes-json --endpoint=http://my-site.com/wp-json - * - * # Invoke with npm script alias, and run against a custom endpoint - * npm run update-default-routes-json -- --endpoint=http://my-site.com/wp-json - * - * This script runs against http://wpapi.local/wp-json by default, but it can be - * run against an arbitrary WordPress REST API endpoint by passing the --endpoint - * argument on the CLI: - * - * @example - * - * # Invoke directly, run against an arbitrary WordPress API root - * ./update-default-routes-json.js --endpoint=http://my-site.com/wp-json - * - * # Invoke with `node` CLI, run against an arbitrary WordPress API root - * node ./update-default-routes-json --endpoint=http://my-site.com/wp-json - * - * Either form will update the `default-routes.json` file in this directory, - * providing that the endpoint data is downloaded successfully. - * - * This script also has some utility for downloading a custom JSON file for your - * own WP REST API-enabled site, so that you can bootstrap your own routes without - * incurring an HTTP request. To output to a different directory than the default - * (which is this directory, `lib/data/`), pass an --output argument on the CLI: - * - * @example - * - * # Output to your current working directory - * ./path/to/this/dir/update-default-routes-json.js --output=. - * - * # Output to an arbitrary absolute path - * ./path/to/this/dir/update-default-routes-json.js --output=/home/mordor/output.json - * - * These command-line flags may be combined, and you will usually want to use - * `--endpoint` alongside `--output` to download your own JSON into your own - * application's directory. The name of the output file can be customized with - * the `--file` option. - */ -'use strict'; - -const agent = require( 'superagent' ); -const fs = require( 'fs' ); -const path = require( 'path' ); -const simplifyObject = require( './simplify-object' ); - -// Parse the arguments object -const argv = require( 'minimist' )( process.argv.slice( 2 ) ); - -if ( argv.h || argv.help ) { - console.log( ` -Available options: - ---endpoint The fully-qualified URI of an API root endpoint to scrape. ---output The directory to which to output the scraped JSON. ---file The filename to which to output the scraped JSON. - -Examples: - -update-default-routes-json \\ - --endpoint=https://wordpress.org/wp-json \\ - --output=lib/data \\ - --file=default-routes.json\n` ); - process.exit(); -} - -// The output directory defaults to the lib/data directory. To customize it, -// specify your own directory with --output=your/output/directory (supports -// both relative and absolute paths) -const outputPath = argv.output ? - // Nested ternary, don't try this at home: this is to support absolute paths - argv.output[ 0 ] === '/' ? argv.output : path.join( process.cwd(), argv.output ) : - // Output to lib/data/ by default - path.resolve( process.cwd(), 'lib', 'data' ); - -// Specify your own API endpoint with --endpoint=http://your-endpoint.com/wp-json -const endpoint = argv.endpoint || 'http://wpapi.local/wp-json'; - -// Specify a custom output file name with --file=custom-api-routes-filename.json -const fileName = argv.file || 'default-routes.json'; - -// This directory will be called to kick off the JSON download: it uses -// superagent internally for HTTP transport that respects HTTP redirects. -const getJSON = ( cbFn ) => { - agent - .get( endpoint ) - .set( 'Accept', 'application/json' ) - .end( ( err, res ) => { - // Inspect the error and then the response to infer various error states - if ( err ) { - console.error( '\nSomething went wrong! Could not download endpoint JSON.' ); - if ( err.status ) { - console.error( 'Error ' + err.status ); - } - if ( err.response && err.response.error ) { - console.error( err.response.error ); - } - return process.exit( 1 ); - } - - if ( res.type !== 'application/json' ) { - console.error( '\nError: expected response type "application/json", got ' + res.type ); - console.error( 'Could not save ' + fileName ); - return process.exit( 1 ); - } - - cbFn( res ); - } ); -}; - -// The only assumption we want to make about the URL is that it should be a web -// URL of _some_ sort, which generally means it has "http" in it somewhere. We -// can't assume much else due to how customizable the location of API root is -// within your WP install. -if ( ! /http/i.test( endpoint ) ) { - console.error( '\nError: ' + endpoint ); - console.error( 'This does not appear to be a valid URL. Please double-check the URL format\n' + - '(should be e.g. "http://your-domain.com/wp-json") and try again.' ); - process.exit( 1 ); -} - -fs.stat( outputPath, ( err, stats ) => { - if ( err || ! stats.isDirectory() ) { - console.error( '\nError: ' + outputPath ); - console.error( 'This is not a valid directory. Please double-check the path and try again.' ); - process.exit( 1 ); - } - - // If we made it this far, our arguments look good! Carry on. - getJSON( ( response ) => { - // Extract the JSON - const endpointJSON = JSON.parse( JSON.stringify( response.body ) ); - // Simplify the JSON structure and pick out the routes dictionary - const slimJSON = simplifyObject( endpointJSON ).routes; - - // Save the file - const outputFilePath = path.join( outputPath, fileName ); - fs.writeFile( outputFilePath, JSON.stringify( slimJSON ), ( err ) => { - if ( err ) { - console.error( '\nSomething went wrong! Could not save ' + outputFilePath ); - return process.exit( 1 ); - } - console.log( '\nSuccessfully saved ' + outputFilePath ); - } ); - } ); -} ); diff --git a/CHANGELOG.md b/changelog.md similarity index 89% rename from CHANGELOG.md rename to changelog.md index d42793db..751ceafb 100644 --- a/CHANGELOG.md +++ b/changelog.md @@ -1,29 +1,13 @@ -# Changelog +--- +layout: page +title: Changelog +permalink: /changelog/ +--- -## v2.0.0 [**alpha**] _Second Toughest in the Infants_ -- **BREAKING**: "Node-style" error-first callbacks (_e.g._ `.get( ( err, data ) => {} )`) are no longer supported. All transport request methods return Promises. -- **BREAKING**: The module exported as `wpapi` no longer includes HTTP methods. Install `superagent` as a peer dependency and `require( 'wpapi/superagent' )` in order to make HTTP requests. -- **BREAKING**: Autodiscovery now either succeeds or fails; a WPAPI instance configured with default routes will no longer be returned. +* TOC +{:toc} -## v1.2.2 _(future release)_ - -- Throw an error early when `.file()` is passed a Buffer object without an accompanying name string, props @cungminh2710 & @mvhirsch - - -## v1.2.1 _Colomb_ - -- Fix issue where `li` was improperly declared as a dev-only dependency, props @el-lsan & @jerolan. - - -## v1.2.0 _Space Is Only Noise_ - -- **BREAKING**: The minimum supported node version is now v8.6, or v8.2.1 with the `--harmony` flag. -- **BREAKING**: `._paging.total` and `._paging.totalPages` response properties are now returned as integers, not strings. -- Bundled route handlers are now available for new first-party endpoints in WordPress 5.0. -- The project now uses Jest and ESLint in place of Mocha, Chai, JSCS and JSHint. Thank you for your years of service, ["nyan" reporter](https://mochajs.org/#nyan)! -- Browser bundle size has been reduced. - ## v1.1.2 _If I Survive_ @@ -68,9 +52,9 @@ v1.0 namesake album _Emotional Technology_ by BT. - Add CHANGELOG.md - Reduce complexity of, and rename, default routes JSON file -- **BREAKING**: Remove third "merge" argument from `.param()` method signature +- BREAKING: Remove third "merge" argument from `.param()` method signature - Document `.settings()` top-level route handler -- **BREAKING**: Return API error objects directly from HTTP transport: only return a transport-level error object in the event of a non-API error +- BREAKING: Return API error objects directly from HTTP transport: only return a transport-level error object in the event of a non-API error - Add `.status()` parameter method mixin - Properly register `.password()` and `.sticky()` parameter mixins - Utilize the HTTP transport methods during auto-discovery process diff --git a/CONTRIBUTING.md b/contributing.md similarity index 71% rename from CONTRIBUTING.md rename to contributing.md index ba4dedb4..c0b46bd2 100644 --- a/CONTRIBUTING.md +++ b/contributing.md @@ -1,4 +1,13 @@ -# Contributing +--- +layout: page +title: Contributing +permalink: /contributing/ +--- + +* TOC +{:toc} + + WordPress is a community effort, as is the WP-API; this client library should be, too. We welcome contributions, however big or small! @@ -10,25 +19,27 @@ New features in this library should be accompanied by unit tests demonstrating t Our tests are broken down into a unit test suite, and an integration test suite. When you add a feature, you should ensure that your changes pass all tests in both suites. And if you find a bug, a test demonstrating that bug is just as useful as a patch that actually solves the problem! -The unit tests can be run without any additional setup with `npm run test:unit`, but running more comprehensive tests (e.g. `npm test`, `npm run test:integration`, etc.) requires additional work as described below in Integration Tests. +The unit tests can be run without any additional setup with `npm run test:unit`, but running more comprehensive tests (e.g. `npm test`, `npm run mocha`, etc.) requires additional work as described below in Integration Tests. ### Integration Tests -In order to run the integration tests you will need to run a specifically-configured local WordPress instance in a virtual machine as described in [wpapi.local](https://github.com/kadamwhite/wpapi.local). Full instructions are provided there, and once that VM is booted and running the integration tests should pass. (You can run the integration suite specifically with the command `npm run test:integration`). +In order to run the integration tests you will need to run a specifically-configured local WordPress instance in a virtual machine as described in [wpapi-vagrant-varietal](https://github.com/kadamwhite/wpapi-vagrant-varietal). Full instructions are provided there, and once that VM is booted and running the integration tests will pass. (You can run the integration suite specifically with the command `npm test:integration`). ### Adding Tests -Adding new code, or submitting a pull request? If it does something, it should have tests! Under the hood we use [Jest](https://jestjs.io/) to run our tests, and write our assertions using [Jest's "expect" BDD syntax](https://jestjs.io/docs/en/expect), *e.g.*: +Adding new code, or submitting a pull request? If it does something, it should have tests! Under the hood we use [Mocha](visionmedia.github.io/mocha/) to run our tests, and write our assertions using [Chai's "expect" BDD syntax](http://chaijs.com/api/bdd/), *e.g.*: ```javascript -expect( wp._options.endpoint ).toBe( 'http://some.url.com/wp-json/' ); +expect( wp._options.endpoint ).to.equal( 'http://some.url.com/wp-json/' ); ``` -**If you are uncomfortable or unfamiliar with writing unit tests,** that's fine! You should feel free to submit a pull request without them. We'll work with you in the PR comments to walk you through how to test the code. +**If you are uncomfortable or unfamiliar with writing unit tests, you should feel free to submit a pull request without them!** We'll work with you in the PR comments to walk you through how to test the code. #### This Function Is Totally Not A Spy -When writing unit tests we want to test only a specific piece of logic, without causing side-effects. To prevent our tests from doing things like actually sending real HTTP requests, we use Jest's [spy](https://jestjs.io/docs/en/jest-object.html#jestspyonobject-methodname), [mocking & stubbing](https://jestjs.io/docs/en/mock-functions) functionality. - +We use [Sinon.js](sinonjs.org/docs/) for [spying on](sinonjs.org/docs/#spies) and [stubbing](http://sinonjs.org/docs/#stubs) functionality. Sinon assertions should also be written with the BDD style, which is enabled via [sinon-chai](https://www.npmjs.org/package/sinon-chai): +```javascript +expect( mockAgent.get ).to.have.been.calledWith( 'url/' ); +``` See the [existing test files](https://github.com/wp-api/node-wpapi/tree/master/tests) for more examples. ## Best Practices for Commits @@ -73,9 +84,39 @@ We rebase feature branches onto master when merging in order to maintain a linea ## Code Syntax & Style -We use [ESLint](https://eslint.org/) to enforce a basic set of code style guidelines and syntax warnings. To run ESLint use the command `npm run lint`; this command will also be run every time you execute `npm test`. +We use [JSCS](https://www.npmjs.org/package/jscs) to enforce a basic set of code style guidelines, and [JSHint](http://jshint.com/) to guard against syntax errors. To run them both execute `npm run lint`; they will also be run every time you execute `npm test`. + +JSCS is a useful tool for enforcing a code style, but isn't flexible enough to cover all guidelines. Note our standard for spacing within function parentheses, which is not enforced mechanically but will be evaluated manually when reviewing pull requests: -We prefer `camelCase` variable and function names, and `UpperCamelCase` constructors. `underscore_case` parameter names may be necessary when working with values returned from or intended to be sent to the WordPress REST API. +```javascript +// Function params and args should be spaced out from the parentheses: +someMethodCall( param1, param2 ); +function newFunction( arg1, arg2 ) {}; +``` + +"When in doubt, space it out," with the following exceptions. + +```javascript +// The space can be omitted when passing function expressions, object literals +// or array literals as arguments: +someMethodCall(function() { + // do stuff +}); +someOtherMethod({ + object: 'no space before an object literal' +}, 'but this String argument still has a space after it' ); +someMethodThatTakesAnArray([ + 'no', + 'leading or trailing', + 'spaces needed' +]); +``` + +We prefer `camelCase` variable and function names, and `UpperCamelCase` constructors. When using the `underscore_case` parameter names that are required by the WordPress API, the following JSHint directive can be used to disable the case enforcement for that particular file: + +```javascript +/*jshint -W106 */// Disable underscore_case warnings in this file +``` ## Documentation diff --git a/documentation/css/main.scss b/css/main.scss similarity index 100% rename from documentation/css/main.scss rename to css/main.scss diff --git a/documentation/.gitignore b/documentation/.gitignore deleted file mode 100644 index 572cf4a6..00000000 --- a/documentation/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -_site -.sass-cache -.jekyll-metadata - -*.lock - -api-reference/ diff --git a/documentation/404.html.combyne b/documentation/404.html.combyne deleted file mode 100644 index 7eda905a..00000000 --- a/documentation/404.html.combyne +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: default ---- - -
- -

404 Not Found

- -

The page you have requested does not exist or has moved.

- -

Perhaps you were looking for one of the resources below:

- -
- -
    - [{%each readmeSections as section %}] -
  1. - [{{{ - section.title - }}}] - [{%if section.hasSubheadings %}] - - [{%endif%}] -
  2. - [{%endeach%}] -
- -
- -
diff --git a/documentation/index.html.combyne b/documentation/index.html.combyne deleted file mode 100644 index b30ccd15..00000000 --- a/documentation/index.html.combyne +++ /dev/null @@ -1,63 +0,0 @@ ---- -layout: default ---- - -
- -

About {{ site.library_title }}

- - [{{{ aboutContents }}}] - -
- -

Readme & Getting Started

- -
    - [{%each readmeSections as section %}] -
  1. - [{{{ - section.title - }}}] - [{%if section.hasSubheadings %}] - - [{%endif%}] -
  2. - [{%endeach%}] -
- -
- -
-

User Guides

- -

Don't see the topic you're looking for? Open an issue to request a guide, or submit your own with a pull request! - -

-
- - [{%-- -

subscribe via RSS

- --%}] - -
diff --git a/documentation/favicon.ico b/favicon.ico similarity index 100% rename from documentation/favicon.ico rename to favicon.ico diff --git a/documentation/favicon.png b/favicon.png similarity index 100% rename from documentation/favicon.png rename to favicon.png diff --git a/documentation/feed.xml b/feed.xml similarity index 100% rename from documentation/feed.xml rename to feed.xml diff --git a/fetch/README.md b/fetch/README.md deleted file mode 100644 index 07cd2a92..00000000 --- a/fetch/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# `wpapi/fetch` - -This endpoint returns a version of the WPAPI library configured to use Fetch for HTTP requests. - -## Installation & Usage - -Install both `wpapi` and `isomorphic-unfetch` using the command `npm install --save wpapi isomorphic-unfetch`. - -```js -import WPAPI from 'wpapi/fetch'; - -// Configure and use WPAPI as normal -const site = new WPAPI( { /* ... */ } ); -``` diff --git a/fetch/fetch-transport.js b/fetch/fetch-transport.js deleted file mode 100644 index 023f1af1..00000000 --- a/fetch/fetch-transport.js +++ /dev/null @@ -1,271 +0,0 @@ -/** - * @module fetch-transport - */ -'use strict'; - -const fetch = require( 'node-fetch' ); -const FormData = require( 'form-data' ); -const fs = require( 'fs' ); - -const objectReduce = require( '../lib/util/object-reduce' ); -const { createPaginationObject } = require( '../lib/pagination' ); - -/** - * Utility method to set a header value on a fetch configuration object. - * - * @method _setHeader - * @private - * @param {Object} config A configuration object of unknown completeness - * @param {string} header String name of the header to set - * @param {string} value Value of the header to set - * @returns {Object} The modified configuration object - */ -const _setHeader = ( config, header, value ) => ( { - ...config, - headers: { - ...( config && config.headers ? config.headers : null ), - [ header ]: value, - }, -} ); - -/** - * Set any provided headers on the outgoing request object. Runs after _auth. - * - * @method _setHeaders - * @private - * @param {Object} config A fetch request configuration object - * @param {Object} options A WPRequest _options object - * @param {Object} A fetch config object, with any available headers set - */ -function _setHeaders( config, options ) { - // If there's no headers, do nothing - if ( ! options.headers ) { - return config; - } - - return objectReduce( - options.headers, - ( config, value, key ) => _setHeader( config, key, value ), - config, - ); -} - -/** - * Conditionally set basic or nonce authentication on a server request object. - * - * @method _auth - * @private - * @param {Object} config A fetch request configuration object - * @param {Object} options A WPRequest _options object - * @param {Boolean} forceAuthentication whether to force authentication on the request - * @param {Object} A fetch request object, conditionally configured to use basic auth - */ -function _auth( config, options, forceAuthentication ) { - // If we're not supposed to authenticate, don't even start - if ( ! forceAuthentication && ! options.auth && ! options.nonce ) { - return config; - } - - // Enable nonce in options for Cookie authentication http://wp-api.org/guides/authentication.html - if ( options.nonce ) { - config.credentials = 'same-origin'; - return _setHeader( config, 'X-WP-Nonce', options.nonce ); - } - - // If no username or no password, can't authenticate - if ( ! options.username || ! options.password ) { - return config; - } - - // Can authenticate: set basic auth parameters on the config - let authorization = `${ options.username }:${ options.password }`; - if ( global.Buffer ) { - authorization = global.Buffer.from( authorization ).toString( 'base64' ); - } else if ( global.btoa ) { - authorization = global.btoa( authorization ); - } - - return _setHeader( config, 'Authorization', `Basic ${ authorization }` ); -} - -// HTTP-Related Helpers -// ==================== - -/** - * Get the response headers as a regular JavaScript object. - * - * @param {Object} response Fetch response object. - */ -function getHeaders( response ) { - const headers = {}; - response.headers.forEach( ( value, key ) => { - headers[ key ] = value; - } ); - return headers; -} - -/** - * Return the body of the request, augmented with pagination information if the - * result is a paged collection. - * - * @private - * @param {WPRequest} wpreq The WPRequest representing the returned HTTP response - * @param {Object} response The fetch response object for the HTTP call - * @returns {Object} The JSON data of the response, conditionally augmented with - * pagination information if the response is a partial collection. - */ -const parseFetchResponse = ( response, wpreq ) => { - // Check if an HTTP error occurred. - if ( ! response.ok ) { - // Extract and return the API-provided error object if the response is - // not ok, i.e. if the error was from the API and not internal to fetch. - return response.json().then( ( err ) => { - // Throw the error object to permit proper error handling. - throw err; - }, () => { - // JSON serialization failed; throw the underlying response. - throw response; - } ); - } - - // If the response is OK, process & return the JSON data. - return response.json().then( ( body ) => { - // Construct a response the pagination helper can understand. - const mockResponse = { - headers: getHeaders( response ), - }; - - const _paging = createPaginationObject( mockResponse, wpreq._options, wpreq.transport ); - if ( _paging ) { - body._paging = _paging; - } - return body; - } ); -}; - -// HTTP Methods: Private HTTP-verb versions -// ======================================== - -const send = ( wpreq, config ) => fetch( - wpreq.toString(), - _setHeaders( _auth( config, wpreq._options ), wpreq._options ) -).then( ( response ) => { - // return response.headers.get( 'Link' ); - return parseFetchResponse( response, wpreq ); -} ); - -/** - * @method get - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpGet( wpreq ) { - return send( wpreq, { - method: 'GET', - } ); -} - -/** - * Invoke an HTTP "POST" request against the provided endpoint - * @method post - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @param {Object} data The data for the POST request - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpPost( wpreq, data = {} ) { - let file = wpreq._attachment; - if ( file ) { - // Handle files provided as a path string - if ( typeof file === 'string' ) { - file = fs.createReadStream( file ); - } - - // Build the form data object - const form = new FormData(); - form.append( 'file', file, wpreq._attachmentName ); - Object.keys( data ).forEach( key => form.append( key, data[ key ] ) ); - - // Fire off the media upload request - return send( wpreq, { - method: 'POST', - redirect: 'follow', - body: form, - } ); - } - - return send( wpreq, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - redirect: 'follow', - body: JSON.stringify( data ), - } ); -} - -/** - * @method put - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @param {Object} data The data for the PUT request - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpPut( wpreq, data = {} ) { - return send( wpreq, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - redirect: 'follow', - body: JSON.stringify( data ), - } ); -} - -/** - * @method delete - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @param {Object} [data] Data to send along with the DELETE request - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpDelete( wpreq, data ) { - const config = { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - redirect: 'follow', - }; - - if ( data ) { - config.body = JSON.stringify( data ); - } - - return send( wpreq, config ); -} - -/** - * @method head - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @returns {Promise} A promise to the header results of the HTTP request - */ -function _httpHead( wpreq ) { - const url = wpreq.toString(); - const config = _setHeaders( _auth( { - method: 'HEAD', - }, wpreq._options, true ), wpreq._options ); - - return fetch( url, config ) - .then( response => getHeaders( response ) ); -} - -module.exports = { - delete: _httpDelete, - get: _httpGet, - head: _httpHead, - post: _httpPost, - put: _httpPut, -}; diff --git a/fetch/index.js b/fetch/index.js deleted file mode 100644 index b9403e54..00000000 --- a/fetch/index.js +++ /dev/null @@ -1,6 +0,0 @@ -const WPAPI = require( '../wpapi' ); -const fetchTransport = require( './fetch-transport' ); -const bindTransport = require( '../lib/bind-transport' ); - -// Bind the fetch-based HTTP transport to the WPAPI constructor -module.exports = bindTransport( WPAPI, fetchTransport ); diff --git a/fetch/tests/.eslintrc.js b/fetch/tests/.eslintrc.js deleted file mode 100644 index bd527d76..00000000 --- a/fetch/tests/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - 'env': { - jest: true, - }, -}; diff --git a/fetch/tests/unit/fetch.js b/fetch/tests/unit/fetch.js deleted file mode 100644 index ba37ae8e..00000000 --- a/fetch/tests/unit/fetch.js +++ /dev/null @@ -1,164 +0,0 @@ -'use strict'; - -const WPAPI = require( '../..' ); - -// HTTP transport, for stubbing -const fetchTransport = require( '../../fetch-transport' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe( 'WPAPI', () => { - - describe( 'constructor', () => { - - describe( 'assigns default HTTP transport', () => { - - it( 'for GET requests', () => { - jest.spyOn( fetchTransport, 'get' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( '' ); - query.get(); - expect( fetchTransport.get ).toHaveBeenCalledWith( query ); - fetchTransport.get.mockRestore(); - } ); - - it( 'for POST requests', () => { - jest.spyOn( fetchTransport, 'post' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( '' ); - const data = {}; - query.create( data ); - expect( fetchTransport.post ).toHaveBeenCalledWith( query, data ); - fetchTransport.post.mockRestore(); - } ); - - it( 'for POST requests', () => { - jest.spyOn( fetchTransport, 'post' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( '' ); - const data = {}; - query.create( data ); - expect( fetchTransport.post ).toHaveBeenCalledWith( query, data ); - fetchTransport.post.mockRestore(); - } ); - - it( 'for PUT requests', () => { - jest.spyOn( fetchTransport, 'put' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( 'a-resource' ); - const data = {}; - query.update( data ); - expect( fetchTransport.put ).toHaveBeenCalledWith( query, data ); - fetchTransport.put.mockRestore(); - } ); - - it( 'for DELETE requests', () => { - jest.spyOn( fetchTransport, 'delete' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( 'a-resource' ); - const data = { - force: true, - }; - query.delete( data ); - expect( fetchTransport.delete ).toHaveBeenCalledWith( query, data ); - fetchTransport.delete.mockRestore(); - } ); - - } ); - - } ); - - describe( '.transport constructor property', () => { - - it( 'is defined', () => { - expect( WPAPI ).toHaveProperty( 'transport' ); - } ); - - it( 'is an object', () => { - expect( typeof WPAPI.transport ).toBe( 'object' ); - } ); - - it( 'has methods for each http transport action', () => { - expect( typeof WPAPI.transport.delete ).toBe( 'function' ); - expect( typeof WPAPI.transport.get ).toBe( 'function' ); - expect( typeof WPAPI.transport.head ).toBe( 'function' ); - expect( typeof WPAPI.transport.post ).toBe( 'function' ); - expect( typeof WPAPI.transport.put ).toBe( 'function' ); - } ); - - it( 'is frozen (properties cannot be modified directly)', () => { - expect( () => { - WPAPI.transport.get = () => {}; - } ).toThrow(); - } ); - - } ); - - describe( '.discover() constructor method', () => { - - beforeEach( () => { - jest.spyOn( fetchTransport, 'get' ).mockImplementation( () => {} ); - } ); - - afterEach( () => { - fetchTransport.get.mockRestore(); - } ); - - it( 'is a function', () => { - expect( WPAPI ).toHaveProperty( 'discover' ); - expect( typeof WPAPI.discover ).toBe( 'function' ); - } ); - - it( 'discovers the API root with a GET request', () => { - const url = 'http://mozarts.house'; - fetchTransport.get.mockImplementation( () => Promise.resolve( { - name: 'Skip Beats', - descrition: 'Just another WordPress weblog', - routes: { - '/': { - _links: { - self: 'http://mozarts.house/wp-json/', - }, - }, - 'list': {}, - 'of': {}, - 'routes': {}, - }, - } ) ); - const prom = WPAPI.discover( url ) - .then( ( result ) => { - expect( result ).toBeInstanceOf( WPAPI ); - expect( result.root().toString() ).toBe( 'http://mozarts.house/wp-json/' ); - expect( fetchTransport.get ).toBeCalledTimes( 1 ); - const indexRequestObject = fetchTransport.get.mock.calls[0][0]; - expect( indexRequestObject.toString() ).toBe( 'http://mozarts.house/?rest_route=%2F' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'throws an error if no API endpoint can be discovered', () => { - const url = 'http://we.made.it/to/mozarts/house'; - fetchTransport.get.mockImplementationOnce( () => Promise.reject( 'Some error' ) ); - const prom = WPAPI.discover( url ) - .catch( ( err ) => { - expect( err ).toBe( 'Some error' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - -} ); diff --git a/index.html b/index.html new file mode 100644 index 00000000..dc32d699 --- /dev/null +++ b/index.html @@ -0,0 +1,150 @@ +--- +layout: default +--- + +
+ +

About {{ site.library_title }}

+ +

node-wpapi is an isomorphic JavaScript client for the WordPress REST API that makes it easy for your JavaScript application to request specific resources from a WordPress website. It uses a query builder-style syntax to let you craft the request being made to REST API endpoints, then returns the API's response to your application as a JSON object. And don't let the name fool you: with Webpack or Browserify, node-wpapi works just as well in the browser as it does on the server!

+

This library is maintained by K. Adam White at Bocoup, with contributions from a great community of WordPress and JavaScript developers.

+

To get started, npm install wpapi or download the browser build and check out "Installation" and "Using the Client" below.

+ + +
+ +

Readme & Getting Started

+ +
    +
  1. + Installation + +
  2. +
  3. + Using the Client + +
  4. +
  5. + Custom Routes + +
  6. +
  7. + Embedding Data +
  8. +
  9. + Collection Pagination + +
  10. +
  11. + Customizing HTTP Request Behavior + +
  12. +
  13. + Authentication + +
  14. +
  15. + Issues +
  16. +
  17. + Contributing +
  18. +
  19. + API Documentation +
  20. +
+ +
+ +
+

User Guides

+ +

Don't see the topic you're looking for? Open an issue to request a guide, or submit your own with a pull request! + +

+
+ + +
diff --git a/lib/bind-transport.js b/lib/bind-transport.js deleted file mode 100644 index 0fbfd86a..00000000 --- a/lib/bind-transport.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Return a new constructor combining the path-building logic of WPAPI with - * a specified HTTP transport layer - * - * This new constructor receives the .discover() static method, and the base - * constructor's .site() static method is overridden to return instances of - * the new transport-specific constructor - * - * See /fetch and /superagent directories - * - * @param {Function} QueryBuilder The base WPAPI query builder constructor - * @param {Object} httpTransport The HTTP transport object - * @returns {Function} A WPAPI constructor with an associated HTTP transport - */ -module.exports = function( QueryBuilder, httpTransport ) { - - // Create a new constructor which inherits from WPAPI, but uses this transport - class WPAPI extends QueryBuilder {} - - WPAPI.transport = Object.create( httpTransport ); - Object.freeze( WPAPI.transport ); - - // Add a version of the base WPAPI.site() static method specific to this new constructor - WPAPI.site = ( endpoint, routes ) => { - return new WPAPI( { - endpoint: endpoint, - routes: routes, - } ); - }; - - /** - * Take an arbitrary WordPress site, deduce the WP REST API root endpoint, query - * that endpoint, and parse the response JSON. Use the returned JSON response - * to instantiate a WPAPI instance bound to the provided site. - * - * @memberof! WPAPI - * @static - * @param {string} url A URL within a REST API-enabled WordPress website - * @returns {Promise} A promise that resolves to a configured WPAPI instance bound - * to the deduced endpoint, or rejected if an endpoint is not found or the - * library is unable to parse the provided endpoint. - */ - WPAPI.discover = ( url ) => { - // Use WPAPI.site to make a request using the defined transport - const req = WPAPI.site( url ).root().param( 'rest_route', '/' ); - return req.get().then( ( apiRootJSON ) => { - const routes = apiRootJSON.routes; - return new WPAPI( { - // Derive the endpoint from the self link for the / root - endpoint: routes['/']._links.self, - // Bootstrap returned WPAPI instance with the discovered routes - routes: routes, - } ); - } ); - }; - - return WPAPI; -}; diff --git a/lib/constructors/wp-request.js b/lib/constructors/wp-request.js deleted file mode 100644 index 3fd9ed5e..00000000 --- a/lib/constructors/wp-request.js +++ /dev/null @@ -1,783 +0,0 @@ -'use strict'; - -const qs = require( 'qs' ); - -const alphaNumericSort = require( '../util/alphanumeric-sort' ); -const keyValToObj = require( '../util/key-val-to-obj' ); -const paramSetter = require( '../util/parameter-setter' ); -const objectReduce = require( '../util/object-reduce' ); -const unique = require( '../util/unique' ); - -/** - * WPRequest is the base API request object constructor - * - * @constructor WPRequest - * @param {Object} options A hash of options for the WPRequest instance - * @param {String} options.endpoint The endpoint URI for the invoking WPAPI instance - * @param {Object} options.transport An object of http transport methods (get, post, etc) - * @param {String} [options.username] A username for authenticating API requests - * @param {String} [options.password] A password for authenticating API requests - * @param {String} [options.nonce] A WP nonce for use with cookie authentication - */ -function WPRequest( options ) { - /** - * Configuration options for the request - * - * @property _options - * @type Object - * @private - * @default {} - */ - this._options = [ - // Whitelisted options keys - 'auth', - 'endpoint', - 'headers', - 'username', - 'password', - 'nonce', - ].reduce( ( localOptions, key ) => { - if ( options && options[ key ] ) { - localOptions[ key ] = options[ key ]; - } - return localOptions; - }, {} ); - - /** - * The HTTP transport methods (.get, .post, .put, .delete, .head) to use for this request - * - * @property transport - * @type {Object} - * @private - */ - this.transport = options && options.transport; - - /** - * A hash of query parameters - * This is used to store the values for supported query parameters like ?_embed - * - * @property _params - * @type Object - * @private - * @default {} - */ - this._params = {}; - - /** - * Methods supported by this API request instance: - * Individual endpoint handlers specify their own subset of supported methods - * - * @property _supportedMethods - * @type Array - * @private - * @default [ 'head', 'get', 'put', 'post', 'delete' ] - */ - this._supportedMethods = [ 'head', 'get', 'put', 'post', 'delete' ]; - - /** - * A hash of values to assemble into the API request path - * (This will be overwritten by each specific endpoint handler constructor) - * - * @property _path - * @type Object - * @private - * @default {} - */ - this._path = {}; -} - -// Private helper methods -// ====================== - -/** - * Identity function for use within invokeAndPromisify() - * @private - */ -const identity = value => value; - -/** - * Process arrays of taxonomy terms into query parameters. - * All terms listed in the arrays will be required (AND behavior). - * - * This method will not be called with any values unless we are handling - * an endpoint with the filter mixin; however, since parameter handling - * (and therefore `_renderQuery()`) are part of WPRequest itself, this - * helper method lives here alongside the code where it is used. - * - * @example - * prepareTaxonomies({ - * tag: [ 'tag1 ', 'tag2' ], // by term slug - * cat: [ 7 ] // by term ID - * }) === { - * tag: 'tag1+tag2', - * cat: '7' - * } - * - * @private - * @param {Object} taxonomyFilters An object of taxonomy term arrays, keyed by taxonomy name - * @returns {Object} An object of prepareFilters-ready query arg and query param value pairs - */ -function prepareTaxonomies( taxonomyFilters ) { - if ( ! taxonomyFilters ) { - return {}; - } - - return objectReduce( - taxonomyFilters, - ( result, terms, key ) => { - // Trim whitespace and concatenate multiple terms with + - result[ key ] = terms - // Coerce term into a string so that trim() won't fail - .map( term => ( term + '' ).trim().toLowerCase() ) - .join( '+' ); - - return result; - }, - {} - ); -} - -/** - * Return an object with any properties with undefined, null or empty string - * values removed. - * - * @example - * - * populated({ - * a: 'a', - * b: '', - * c: null - * }); // { a: 'a' } - * - * @private - * @param {Object} obj An object of key/value pairs - * @returns {Object} That object with all empty values removed - */ -const populated = ( obj ) => { - if ( ! obj ) { - return obj; - } - return objectReduce( - obj, - ( values, val, key ) => { - if ( val !== undefined && val !== null && val !== '' ) { - values[ key ] = val; - } - return values; - }, - {} - ); -}; - -/** - * Assert whether a provided URL component is "valid" by checking it against - * an array of registered path component validator methods for that level of - * the URL path. - * - * @private - * @param {object[]} levelDefinitions An array of Level Definition objects - * @param {string} levelContents The URL path string that has been specified - * for use on the provided level - * @returns {boolean} Whether the provided input matches any of the provided - * level validation functions - */ -const validatePathLevel = ( levelDefinitions, levelContents ) => { - // One "level" may have multiple options, as a route tree is a branching - // structure. We consider a level "valid" if the provided levelContents - // match any of the available validators. - const valid = levelDefinitions.reduce( ( anyOptionValid, levelOption ) => { - if ( ! levelOption.validate ) { - // If there is no validator function, the level is implicitly valid - return true; - } - return anyOptionValid || levelOption.validate( levelContents ); - }, false ); - - if ( ! valid ) { - throw new Error( [ - 'Invalid path component:', - levelContents, - // awkward pluralization support: - 'does not match' + ( levelDefinitions.length > 1 ? ' any of' : '' ), - levelDefinitions.reduce( - ( components, levelOption ) => components.concat( levelOption.component ), - [] - ).join( ', ' ), - ].join( ' ' ) ); - } -}; - -// (Semi-)Private Prototype Methods -// ================================ - -/** - * Process the endpoint query's filter objects into a valid query string. - * Nested objects and Array properties are rendered with indexed array syntax. - * - * @example - * _renderQuery({ p1: 'val1', p2: 'val2' }); // ?p1=val1&p2=val2 - * _renderQuery({ obj: { prop: 'val' } }); // ?obj[prop]=val - * _renderQuery({ arr: [ 'val1', 'val2' ] }); // ?arr[0]=val1&arr[1]=val2 - * - * @private - * - * @method _renderQuery - * @returns {String} A query string representing the specified filter parameters - */ -WPRequest.prototype._renderQuery = function() { - // Build the full query parameters object - const queryParams = { - ...populated( this._params ), - }; - - // Prepare any taxonomies and merge with other filter values - const taxonomies = prepareTaxonomies( this._taxonomyFilters ); - queryParams.filter = { - ...populated( this._filters ), - ...taxonomies, - }; - - // Parse query parameters object into a query string, sorting the object - // properties by alphabetical order (consistent property ordering can make - // for easier caching of request URIs) - const queryString = qs.stringify( queryParams, { arrayFormat: 'brackets' } ) - .split( '&' ) - .sort() - .join( '&' ); - - // Check if the endpoint contains a previous query and set the query character accordingly. - const queryCharacter = /\?/.test( this._options.endpoint ) ? '&' : '?'; - - // Prepend a "?" (or a "&") if a query is present, and return. - return ( queryString === '' ) ? '' : queryCharacter + queryString; -}; - -/** - * Validate & assemble a path string from the request object's _path - * - * @private - * @returns {String} The rendered path - */ -WPRequest.prototype._renderPath = function() { - // Call validatePath: if the provided path components are not well-formed, - // an error will be thrown - this.validatePath(); - - const pathParts = this._path; - const orderedPathParts = Object.keys( pathParts ) - .sort( ( a, b ) => { - const intA = parseInt( a, 10 ); - const intB = parseInt( b, 10 ); - return intA - intB; - } ) - .map( pathPartKey => pathParts[ pathPartKey ] ); - - // Combine all parts of the path together, filtered to omit any components - // that are unspecified or empty strings, to create the full path template - const path = [ - this._namespace, - ].concat( orderedPathParts ).filter( identity ).join( '/' ); - - return path; -}; - -// Public Prototype Methods -// ======================== - -/** - * Parse the request into a WordPress API request URI string - * - * @method - * @returns {String} The URI for the HTTP request to be sent - */ -WPRequest.prototype.toString = function() { - // Render the path to a string - const path = this._renderPath(); - - // Render the query string - const queryStr = this._renderQuery(); - - return this._options.endpoint + path + queryStr; -}; - -/** - * Set a component of the resource URL itself (as opposed to a query parameter) - * - * If a path component has already been set at this level, throw an error: - * requests are meant to be transient, so any re-writing of a previously-set - * path part value is likely to be a mistake. - * - * @method - * @chainable - * @param {Number|String} level A "level" of the path to set, e.g. "1" or "2" - * @param {Number|String} val The value to set at that path part level - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.setPathPart = function( level, val ) { - if ( this._path[ level ] ) { - throw new Error( 'Cannot overwrite value ' + this._path[ level ] ); - } - this._path[ level ] = val; - - return this; -}; - -/** - * Validate whether the specified path parts are valid for this endpoint - * - * "Path parts" are non-query-string URL segments, like "some" "path" in the URL - * `mydomain.com/some/path?and=a&query=string&too`. Because a well-formed path - * is necessary to execute a successful API request, we throw an error if the - * user has omitted a value (such as `/some/[missing component]/url`) or has - * provided a path part value that does not match the regular expression the - * API uses to goven that segment. - * - * @method - * @chainable - * @returns {WPRequest} The WPRequest instance (for chaining), if no errors were found - */ -WPRequest.prototype.validatePath = function() { - // Iterate through all _specified_ levels of this endpoint - const specifiedLevels = Object.keys( this._path ) - .map( level => parseInt( level, 10 ) ) - .filter( pathPartKey => ! isNaN( pathPartKey ) ); - - const maxLevel = Math.max.apply( null, specifiedLevels ); - - // Ensure that all necessary levels are specified - const path = []; - let valid = true; - - for ( let level = 0; level <= maxLevel; level++ ) { - - if ( ! this._levels || ! this._levels[ level ] ) { - continue; - } - - if ( this._path[ level ] ) { - // Validate the provided path level against all available path validators - validatePathLevel( this._levels[ level ], this._path[ level ] ); - - // Add the path value to the array - path.push( this._path[ level ] ); - } else { - path.push( ' ??? ' ); - valid = false; - } - } - - if ( ! valid ) { - throw new Error( 'Incomplete URL! Missing component: /' + path.join( '/' ) ); - } - - return this; -}; - -/** - * Set a parameter to render into the final query URI. - * - * @method - * @chainable - * @param {String|Object} props The name of the parameter to set, or an object containing - * parameter keys and their corresponding values - * @param {String|Number|Array} [value] The value of the parameter being set - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.param = function( props, value ) { - if ( ! props || typeof props === 'string' && value === undefined ) { - // We have no property to set, or no value to set for that property - return this; - } - - // We can use the same iterator function below to handle explicit key-value - // pairs if we convert them into to an object we can iterate over: - if ( typeof props === 'string' ) { - props = keyValToObj( props, value ); - } - - // Iterate through the properties - Object.keys( props ).forEach( ( key ) => { - let value = props[ key ]; - - // Arrays should be de-duped and sorted - if ( Array.isArray( value ) ) { - value = unique( value ).sort( alphaNumericSort ); - } - - // Set the value - this._params[ key ] = value; - } ); - - return this; -}; - -// Globally-applicable parameters that impact the shape of the request or response -// =============================================================================== - -/** - * Set the context of the request. Used primarily to expose private values on a - * request object by setting the context to "edit". - * - * @method - * @chainable - * @param {String} context The context to set on the request - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.context = paramSetter( 'context' ); - -/** - * Convenience wrapper for `.context( 'edit' )` - * - * @method - * @chainable - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.edit = function() { - return this.context( 'edit' ); -}; - -/** - * Return embedded resources as part of the response payload. - * - * @method - * @chainable - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.embed = function() { - return this.param( '_embed', true ); -}; - -// Parameters supported by all/nearly all default collections -// ========================================================== - -/** - * Set the pagination of a request. Use in conjunction with `.perPage()` for explicit - * pagination handling. (The number of pages in a response can be retrieved from the - * response's `_paging.totalPages` property.) - * - * @method - * @chainable - * @param {Number} pageNumber The page number of results to retrieve - * @returns The request instance (for chaining) - */ -WPRequest.prototype.page = paramSetter( 'page' ); - -/** - * Set the number of items to be returned in a page of responses. - * - * @method - * @chainable - * @param {Number} itemsPerPage The number of items to return in one page of results - * @returns The request instance (for chaining) - */ -WPRequest.prototype.perPage = paramSetter( 'per_page' ); - -/** - * Set an arbitrary offset to retrieve items from a specific point in a collection. - * - * @method - * @chainable - * @param {Number} offsetNumber The number of items by which to offset the response - * @returns The request instance (for chaining) - */ -WPRequest.prototype.offset = paramSetter( 'offset' ); - -/** - * Change the sort direction of a returned collection - * - * @example order comments chronologically (oldest first) - * - * site.comments().order( 'asc' )... - * - * @method - * @chainable - * @param {String} direction The order to use when sorting the response - * @returns The request instance (for chaining) - */ -WPRequest.prototype.order = paramSetter( 'order' ); - -/** - * Order a collection by a specific field - * - * @method - * @chainable - * @param {String} field The field by which to order the response - * @returns The request instance (for chaining) - */ -WPRequest.prototype.orderby = paramSetter( 'orderby' ); - -/** - * Filter results to those matching the specified search terms. - * - * @method - * @chainable - * @param {String} searchString A string to search for within post content - * @returns The request instance (for chaining) - */ -WPRequest.prototype.search = paramSetter( 'search' ); - -/** - * Include specific resource IDs in the response collection. - * - * @method - * @chainable - * @param {Number|Number[]} ids An ID or array of IDs to include - * @returns The request instance (for chaining) - */ -WPRequest.prototype.include = paramSetter( 'include' ); - -/** - * Exclude specific resource IDs in the response collection. - * - * @method - * @chainable - * @param {Number|Number[]} ids An ID or array of IDs to exclude - * @returns The request instance (for chaining) - */ -WPRequest.prototype.exclude = paramSetter( 'exclude' ); - -/** - * Query a collection for members with a specific slug. - * - * @method - * @chainable - * @param {String} slug A post slug (slug), e.g. "hello-world" - * @returns The request instance (for chaining) - */ -WPRequest.prototype.slug = paramSetter( 'slug' ); - -// HTTP Transport Prototype Methods -// ================================ - -// Chaining methods -// ================ - -/** - * Set the namespace of the request, e.g. to specify the API root for routes - * registered by wp core v2 ("wp/v2") or by any given plugin. Any previously- - * set namespace will be overwritten by subsequent calls to the method. - * - * @method - * @chainable - * @param {String} namespace A namespace string, e.g. "wp/v2" - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.namespace = function( namespace ) { - this._namespace = namespace; - return this; -}; - -/** - * Set a request to use authentication, and optionally provide auth credentials - * - * If auth credentials were already specified when the WPAPI instance was created, calling - * `.auth` on the request chain will set that request to use the existing credentials: - * - * @example use existing credentials - * - * request.auth().get... - * - * Alternatively, a username & password (or nonce) can be explicitly passed into `.auth`: - * - * @example use explicit basic authentication credentials - * - * request.auth({ - * username: 'admin', - * password: 'super secure' - * }).get... - * - * @example use a nonce for cookie authentication - * - * request.auth({ - * nonce: 'somenonce' - * })... - * - * @method - * @chainable - * @param {Object} credentials An object with 'username' and 'password' string - * properties, or else a 'nonce' property - * @param {String} [credentials.username] A WP-API Basic HTTP Authentication username - * @param {String} [credentials.password] A WP-API Basic HTTP Authentication password - * @param {String} [credentials.nonce] A WP nonce for use with cookie authentication - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.auth = function( credentials ) { - if ( typeof credentials === 'object' ) { - if ( typeof credentials.username === 'string' ) { - this._options.username = credentials.username; - } - - if ( typeof credentials.password === 'string' ) { - this._options.password = credentials.password; - } - - if ( credentials.nonce ) { - this._options.nonce = credentials.nonce; - } - } - - // Set the "auth" options flag that will force authentication on this request - this._options.auth = true; - - return this; -}; - -/** - * Specify a file or a file buffer to attach to the request, for use when - * creating a new Media item - * - * @example within a server context - * - * wp.media() - * // Pass .file() the file system path to a file to upload - * .file( '/path/to/file.jpg' ) - * .create({})... - * - * wp.media() - * // Pass .file() an image as a Buffer object, and a filename string - * .file( imgBuffer, 'desired-title.jpg' ) - * .create({})... - * - * @example within a browser context - * - * wp.media() - * // Pass .file() the file reference from an HTML file input - * .file( document.querySelector( 'input[type="file"]' ).files[0] ) - * .create({})... - * - * @method - * @chainable - * @param {string|object} file A path to a file (in Node) or an file object - * (Node or Browser) to attach to the request - * @param {string} [name] A filename to use for the file, required when - * providing file data as a Buffer object - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.file = function( file, name ) { - if ( global.Buffer && file instanceof global.Buffer && ! name ) { - throw new Error( '.file(): File name is a required argument when uploading a Buffer' ); - } - this._attachment = file; - // Explicitly set to undefined if not provided, to override any previously- - // set attachment name property that might exist from a prior `.file()` call - this._attachmentName = name ? name : undefined; - return this; -}; - -// HTTP Methods: Public Interface -// ============================== - -/** - * Specify one or more headers to send with the dispatched HTTP request. - * - * @example Set a single header to be used on this request - * - * request.setHeaders( 'Authorization', 'Bearer trustme' )... - * - * @example Set multiple headers to be used by this request - * - * request.setHeaders({ - * Authorization: 'Bearer comeonwereoldfriendsright', - * 'Accept-Language': 'en-CA' - * })... - * - * @since 1.1.0 - * @method - * @chainable - * @param {String|Object} headers The name of the header to set, or an object of - * header names and their associated string values - * @param {String} [value] The value of the header being set - * @returns {WPRequest} The WPRequest instance (for chaining) - */ -WPRequest.prototype.setHeaders = function( headers, value ) { - // We can use the same iterator function below to handle explicit key-value - // pairs if we convert them into to an object we can iterate over: - if ( typeof headers === 'string' ) { - headers = keyValToObj( headers, value ); - } - - this._options.headers = { - ...( this._options.headers || {} ), - ...headers, - }; - - return this; -}; - -/** - * Get (download the data for) the specified resource - * - * @method - * @async - * @returns {Promise} A promise to the results of the HTTP request - */ -WPRequest.prototype.get = function() { - return this.transport.get( this ); -}; - -/** - * Get the headers for the specified resource - * - * @method - * @async - * @returns {Promise} A promise to the header results of the HTTP request - */ -WPRequest.prototype.headers = function() { - return this.transport.head( this ); -}; - -/** - * Create the specified resource with the provided data - * - * This is the public interface for creating POST requests - * - * @method - * @async - * @param {Object} data The data for the POST request - * @returns {Promise} A promise to the results of the HTTP request - */ -WPRequest.prototype.create = function( data ) { - return this.transport.post( this, data ); -}; - -/** - * Update the specified resource with the provided data - * - * This is the public interface for creating PUT requests - * - * @method - * @async - * @private - * @param {Object} data The data for the PUT request - * @returns {Promise} A promise to the results of the HTTP request - */ -WPRequest.prototype.update = function( data ) { - return this.transport.put( this, data ); -}; - -/** - * Delete the specified resource - * - * @method - * @async - * @param {Object} [data] Data to send along with the DELETE request - * @returns {Promise} A promise to the results of the HTTP request - */ -WPRequest.prototype.delete = function( data ) { - return this.transport.delete( this, data ); -}; - -/** - * Calling .then on a query chain will invoke the query as a GET and return a promise - * - * @method - * @async - * @param {Function} [successCallback] A callback to handle the data returned from the GET request - * @param {Function} [failureCallback] A callback to handle any errors encountered by the request - * @returns {Promise} A promise to the results of the HTTP request - */ -WPRequest.prototype.then = function( successCallback, failureCallback ) { - return this.transport.get( this ).then( successCallback, failureCallback ); -}; - -module.exports = WPRequest; diff --git a/lib/data/.eslintrc.js b/lib/data/.eslintrc.js deleted file mode 100644 index 7214a57c..00000000 --- a/lib/data/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - 'rules': { - 'no-console': [ 'off' ], - }, -}; diff --git a/lib/data/default-routes.json b/lib/data/default-routes.json deleted file mode 100644 index f40b3719..00000000 --- a/lib/data/default-routes.json +++ /dev/null @@ -1 +0,0 @@ -{"/":{"namespace":"","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"context":{}}}]},"/oembed/1.0":{"namespace":"oembed/1.0","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"namespace":{},"context":{}}}]},"/oembed/1.0/embed":{"namespace":"oembed/1.0","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"url":{},"format":{},"maxwidth":{}}}]},"/oembed/1.0/proxy":{"namespace":"oembed/1.0","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"url":{},"format":{},"maxwidth":{},"maxheight":{},"discover":{}}}]},"/wp/v2":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"namespace":{},"context":{}}}]},"/wp/v2/posts":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"after":{},"author":{},"author_exclude":{},"before":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{},"slug":{},"status":{},"categories":{},"categories_exclude":{},"tags":{},"tags_exclude":{},"sticky":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/posts/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{},"password":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/posts/(?P[\\d]+)/revisions":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"context":{},"page":{},"per_page":{},"search":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{}}}]},"/wp/v2/posts/(?P[\\d]+)/revisions/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","DELETE"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"id":{},"context":{}}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/posts/(?P[\\d]+)/autosaves":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"context":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/posts/(?P[\\d]+)/autosaves/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"id":{},"context":{}}}]},"/wp/v2/pages":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"after":{},"author":{},"author_exclude":{},"before":{},"exclude":{},"include":{},"menu_order":{},"offset":{},"order":{},"orderby":{},"parent":{},"parent_exclude":{},"slug":{},"status":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/pages/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{},"password":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/pages/(?P[\\d]+)/revisions":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"context":{},"page":{},"per_page":{},"search":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{}}}]},"/wp/v2/pages/(?P[\\d]+)/revisions/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","DELETE"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"id":{},"context":{}}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/pages/(?P[\\d]+)/autosaves":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"context":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/pages/(?P[\\d]+)/autosaves/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"id":{},"context":{}}}]},"/wp/v2/media":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"after":{},"author":{},"author_exclude":{},"before":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{},"parent":{},"parent_exclude":{},"slug":{},"status":{},"media_type":{},"mime_type":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/media/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/blocks":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"after":{},"before":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{},"slug":{},"status":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/blocks/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{},"password":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/blocks/(?P[\\d]+)/autosaves":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"context":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/blocks/(?P[\\d]+)/autosaves/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"parent":{},"id":{},"context":{}}}]},"/wp/v2/types":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"context":{}}}]},"/wp/v2/types/(?P[\\w-]+)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"type":{},"context":{}}}]},"/wp/v2/statuses":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"context":{}}}]},"/wp/v2/statuses/(?P[\\w-]+)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"status":{},"context":{}}}]},"/wp/v2/taxonomies":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"context":{},"type":{}}}]},"/wp/v2/taxonomies/(?P[\\w-]+)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"taxonomy":{},"context":{}}}]},"/wp/v2/categories":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"exclude":{},"include":{},"order":{},"orderby":{},"hide_empty":{},"parent":{},"post":{},"slug":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/categories/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/tags":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{},"hide_empty":{},"post":{},"slug":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/tags/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/users":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{},"slug":{},"roles":{},"who":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/users/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/users/me":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"context":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/comments":{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"after":{},"author":{},"author_exclude":{},"author_email":{},"before":{},"exclude":{},"include":{},"offset":{},"order":{},"orderby":{},"parent":{},"parent_exclude":{},"post":{},"status":{},"type":{},"password":{}}},{"methods":["POST"],"args":{}}]},"/wp/v2/comments/(?P[\\d]+)":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH","DELETE"],"endpoints":[{"methods":["GET"],"args":{"id":{},"context":{},"password":{}}},{"methods":["POST","PUT","PATCH"],"args":{}},{"methods":["DELETE"],"args":{}}]},"/wp/v2/search":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"type":{},"subtype":{}}}]},"/wp/v2/block-renderer/(?Pcore/block)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"name":{},"context":{},"attributes":{},"post_id":{}}}]},"/wp/v2/block-renderer/(?Pcore/latest-comments)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"name":{},"context":{},"attributes":{},"post_id":{}}}]},"/wp/v2/block-renderer/(?Pcore/archives)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"name":{},"context":{},"attributes":{},"post_id":{}}}]},"/wp/v2/block-renderer/(?Pcore/categories)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"name":{},"context":{},"attributes":{},"post_id":{}}}]},"/wp/v2/block-renderer/(?Pcore/latest-posts)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"name":{},"context":{},"attributes":{},"post_id":{}}}]},"/wp/v2/block-renderer/(?Pcore/shortcode)":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"name":{},"context":{},"attributes":{},"post_id":{}}}]},"/wp/v2/settings":{"namespace":"wp/v2","methods":["GET","POST","PUT","PATCH"],"endpoints":[{"methods":["GET"],"args":{}},{"methods":["POST","PUT","PATCH"],"args":{}}]},"/wp/v2/themes":{"namespace":"wp/v2","methods":["GET"],"endpoints":[{"methods":["GET"],"args":{"context":{},"page":{},"per_page":{},"search":{},"status":{}}}]}} \ No newline at end of file diff --git a/lib/endpoint-factories.js b/lib/endpoint-factories.js deleted file mode 100644 index 159b371e..00000000 --- a/lib/endpoint-factories.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Take a WP route string (with PCRE named capture groups), `such as /author/(?P\d+)`, - * and generate request handler factory methods for each represented endpoint. - * - * @module endpoint-factories - */ -'use strict'; - -const createResourceHandlerSpec = require( './resource-handler-spec' ).create; -const createEndpointRequest = require( './endpoint-request' ).create; -const objectReduce = require( './util/object-reduce' ); - -/** - * Given an array of route definitions and a specific namespace for those routes, - * recurse through the node tree representing all possible routes within the - * provided namespace to define path value setters (and corresponding property - * validators) for all possible variants of each resource's API endpoints. - * - * @method generate - * @param {string} namespace The namespace string for these routes - * @param {object} routesByNamespace A dictionary of namespace - route definition - * object pairs as generated from buildRouteTree, - * where each route definition object is a dictionary - * keyed by route definition strings - * @returns {object} A dictionary of endpoint request handler factories - */ -function generateEndpointFactories( routesByNamespace ) { - - return objectReduce( routesByNamespace, ( namespaces, routeDefinitions, namespace ) => { - - // Create - namespaces[ namespace ] = objectReduce( routeDefinitions, ( handlers, routeDef, resource ) => { - - const handlerSpec = createResourceHandlerSpec( routeDef, resource ); - - const EndpointRequest = createEndpointRequest( handlerSpec, resource, namespace ); - - // "handler" object is now fully prepared; create the factory method that - // will instantiate and return a handler instance - handlers[ resource ] = function( options ) { - return new EndpointRequest( { - ...this._options, - ...options, - } ); - }; - - // Expose the constructor as a property on the factory function, so that - // auto-generated endpoint request constructors may be further customized - // when needed - handlers[ resource ].Ctor = EndpointRequest; - - return handlers; - }, {} ); - - return namespaces; - }, {} ); -} - -module.exports = { - generate: generateEndpointFactories, -}; diff --git a/lib/endpoint-request.js b/lib/endpoint-request.js deleted file mode 100644 index 0b0950c9..00000000 --- a/lib/endpoint-request.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @module endpoint-request - */ -'use strict'; - -const WPRequest = require( './constructors/wp-request' ); -const mixins = require( './mixins' ); - -const applyMixin = require( './util/apply-mixin' ); - -/** - * Create an endpoint request handler constructor for a specific resource tree - * - * @method create - * @param {Object} handlerSpec A resource handler specification object - * @param {String} resource The root resource of requests created from the returned factory - * @param {String} namespace The namespace string for the returned factory's handlers - * @returns {Function} A constructor inheriting from {@link WPRequest} - */ -function createEndpointRequest( handlerSpec, resource, namespace ) { - - // Create the constructor function for this endpoint - class EndpointRequest extends WPRequest { - constructor( options ) { - super( options ); - - /** - * Semi-private instance property specifying the available URL path options - * for this endpoint request handler, keyed by ascending whole numbers. - * - * @property _levels - * @type {object} - * @private - */ - this._levels = handlerSpec._levels; - - // Configure handler for this endpoint's root URL path & set namespace - this - .setPathPart( 0, resource ) - .namespace( namespace ); - } - } - - // Mix in all available shortcut methods for GET request query parameters that - // are valid within this endpoint tree - if ( typeof handlerSpec._getArgs === 'object' ) { - Object.keys( handlerSpec._getArgs ).forEach( ( supportedQueryParam ) => { - const mixinsForParam = mixins[ supportedQueryParam ]; - - // Only proceed if there is a mixin available AND the specified mixins will - // not overwrite any previously-set prototype method - if ( typeof mixinsForParam === 'object' ) { - Object.keys( mixinsForParam ).forEach( ( methodName ) => { - applyMixin( EndpointRequest.prototype, methodName, mixinsForParam[ methodName ] ); - } ); - } - } ); - } - - Object.keys( handlerSpec._setters ).forEach( ( setterFnName ) => { - // Only assign setter functions if they do not overwrite preexisting methods - if ( ! EndpointRequest.prototype[ setterFnName ] ) { - EndpointRequest.prototype[ setterFnName ] = handlerSpec._setters[ setterFnName ]; - } - } ); - - return EndpointRequest; -} - -module.exports = { - create: createEndpointRequest, -}; diff --git a/lib/mixins/filters.js b/lib/mixins/filters.js deleted file mode 100644 index 50c61de2..00000000 --- a/lib/mixins/filters.js +++ /dev/null @@ -1,195 +0,0 @@ -/** - * @module mixins/filters - */ -'use strict'; - -const alphaNumericSort = require( '../util/alphanumeric-sort' ); -const keyValToObj = require( '../util/key-val-to-obj' ); -const unique = require( '../util/unique' ); - -/** - * Filter methods that can be mixed in to a request constructor's prototype to - * allow that request to take advantage of the `?filter[]=` aliases for WP_Query - * parameters for collection endpoints, when available. - * - * @mixin filters - */ -const filterMixins = {}; - -// Filter Methods -// ============== - -/** - * Specify key-value pairs by which to filter the API results (commonly used - * to retrieve only posts meeting certain criteria, such as posts within a - * particular category or by a particular author). - * - * @example - * - * // Set a single property: - * wp.filter( 'post_type', 'cpt_event' )... - * - * // Set multiple properties at once: - * wp.filter({ - * post_status: 'publish', - * category_name: 'news' - * })... - * - * // Chain calls to .filter(): - * wp.filter( 'post_status', 'publish' ).filter( 'category_name', 'news' )... - * - * @method filter - * @chainable - * @param {String|Object} props A filter property name string, or object of name/value pairs - * @param {String|Number|Array} [value] The value(s) corresponding to the provided filter property - * @returns The request instance (for chaining) - */ -filterMixins.filter = function( props, value ) { - if ( ! props || typeof props === 'string' && value === undefined ) { - // We have no filter to set, or no value to set for that filter - return this; - } - - // convert the property name string `props` and value `value` into an object - if ( typeof props === 'string' ) { - props = keyValToObj( props, value ); - } - - this._filters = { - ...this._filters, - ...props, - }; - - return this; -}; - -/** - * Restrict the query results to posts matching one or more taxonomy terms. - * - * @method taxonomy - * @chainable - * @param {String} taxonomy The name of the taxonomy to filter by - * @param {String|Number|Array} term A string or integer, or array thereof, representing terms - * @returns The request instance (for chaining) - */ -filterMixins.taxonomy = function( taxonomy, term ) { - const termIsArray = Array.isArray( term ); - - const termIsNumber = termIsArray ? - term.reduce( - ( allAreNumbers, term ) => allAreNumbers && typeof term === 'number', - true - ) : - typeof term === 'number'; - - const termIsString = termIsArray ? - term.reduce( - ( allAreStrings, term ) => allAreStrings && typeof term === 'string', - true - ) : - typeof term === 'string'; - - if ( ! termIsString && ! termIsNumber ) { - throw new Error( 'term must be a number, string, or array of numbers or strings' ); - } - - if ( taxonomy === 'category' ) { - if ( termIsString ) { - // Query param for filtering by category slug is "category_name" - taxonomy = 'category_name'; - } else { - // The boolean check above ensures that if taxonomy === 'category' and - // term is not a string, then term must be a number and therefore an ID: - // Query param for filtering by category ID is "cat" - taxonomy = 'cat'; - } - } else if ( taxonomy === 'post_tag' ) { - // tag is used in place of post_tag in the public query variables - taxonomy = 'tag'; - } - - // Ensure the taxonomy filters object is available - this._taxonomyFilters = this._taxonomyFilters || {}; - - // Ensure there's an array of terms available for this taxonomy - const taxonomyTerms = ( this._taxonomyFilters[ taxonomy ] || [] ) - // Insert the provided terms into the specified taxonomy's terms array - .concat( term ) - // Sort array - .sort( alphaNumericSort ); - - // De-dupe - this._taxonomyFilters[ taxonomy ] = unique( taxonomyTerms, true ); - - return this; -}; - -/** - * Query for posts published in a given year. - * - * @method year - * @chainable - * @param {Number} year integer representation of year requested - * @returns The request instance (for chaining) - */ -filterMixins.year = function( year ) { - return filterMixins.filter.call( this, 'year', year ); -}; - -/** - * Query for posts published in a given month, either by providing the number - * of the requested month (e.g. 3), or the month's name as a string (e.g. "March") - * - * @method month - * @chainable - * @param {Number|String} month Integer for month (1) or month string ("January") - * @returns The request instance (for chaining) - */ -filterMixins.month = function( month ) { - let monthDate; - if ( typeof month === 'string' ) { - // Append a arbitrary day and year to the month to parse the string into a Date - monthDate = new Date( Date.parse( month + ' 1, 2012' ) ); - - // If the generated Date is NaN, then the passed string is not a valid month - if ( isNaN( monthDate ) ) { - return this; - } - - // JS Dates are 0 indexed, but the WP API requires a 1-indexed integer - month = monthDate.getMonth() + 1; - } - - // If month is a Number, add the monthnum filter to the request - if ( typeof month === 'number' ) { - return filterMixins.filter.call( this, 'monthnum', month ); - } - - return this; -}; - -/** - * Add the day filter into the request to retrieve posts for a given day - * - * @method day - * @chainable - * @param {Number} day Integer representation of the day requested - * @returns The request instance (for chaining) - */ -filterMixins.day = function( day ) { - return filterMixins.filter.call( this, 'day', day ); -}; - -/** - * Specify that we are requesting a page by its path (specific to Page resources) - * - * @method path - * @chainable - * @param {String} path The root-relative URL path for a page - * @returns The request instance (for chaining) - */ -filterMixins.path = function( path ) { - return filterMixins.filter.call( this, 'pagename', path ); -}; - -module.exports = filterMixins; diff --git a/lib/mixins/index.js b/lib/mixins/index.js deleted file mode 100644 index 4845a7cb..00000000 --- a/lib/mixins/index.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * This module defines a mapping between supported GET request query parameter - * arguments and their corresponding mixin, if available. - */ -'use strict'; - -const filterMixins = require( './filters' ); -const parameterMixins = require( './parameters' ); - -// `.context`, `.embed`, and `.edit` (a shortcut for `context(edit, true)`) are -// supported by default in WPRequest, as is the base `.param` method. Any GET -// argument parameters not covered here must be set directly by using `.param`. - -// The initial mixins we define are the ones where either a single property -// accepted by the API endpoint corresponds to multiple individual mixin -// functions, or where the name we use for the function diverges from that -// of the query parameter that the mixin sets. -const mixins = { - categories: { - categories: parameterMixins.categories, - /** @deprecated use .categories() */ - category: parameterMixins.category, - }, - categories_exclude: { - excludeCategories: parameterMixins.excludeCategories, - }, - tags: { - tags: parameterMixins.tags, - /** @deprecated use .tags() */ - tag: parameterMixins.tag, - }, - tags_exclude: { - excludeTags: parameterMixins.excludeTags, - }, - filter: filterMixins, - post: { - post: parameterMixins.post, - /** @deprecated use .post() */ - forPost: parameterMixins.post, - }, -}; - -// All of these parameter mixins use a setter function named identically to the -// property that the function sets, but they must still be provided in wrapper -// objects so that the mixin can be `.assign`ed correctly: wrap & assign each -// setter to the mixins dictionary object. -[ - 'after', - 'author', - 'before', - 'parent', - 'password', - 'status', - 'sticky', -].forEach( ( mixinName ) => { - mixins[ mixinName ] = {}; - mixins[ mixinName ][ mixinName ] = parameterMixins[ mixinName ]; -} ); - -module.exports = mixins; diff --git a/lib/mixins/parameters.js b/lib/mixins/parameters.js deleted file mode 100644 index a9bf32ba..00000000 --- a/lib/mixins/parameters.js +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Filter methods that can be mixed in to a request constructor's prototype to - * allow that request to take advantage of top-level query parameters for - * collection endpoints. These are most relevant to posts, pages and CPTs, but - * pagination helpers are applicable to any collection. - * - * @module mixins/parameters - */ -'use strict'; - -const paramSetter = require( '../util/parameter-setter' ); -const argumentIsNumeric = require( '../util/argument-is-numeric' ); - -/** - * @mixin parameters - */ -const parameterMixins = {}; - -const filters = require( './filters' ); -// Needed for .author mixin, as author by ID is a parameter and by Name is a filter -const filter = filters.filter; -// Needed for .tag and .category mixin, for deprecated query-by-slug support -const taxonomy = filters.taxonomy; - -// Parameter Methods -// ================= - -/** - * Query for posts by a specific author. - * This method will replace any previous 'author' query parameters that had been set. - * - * Note that this method will either set the "author" top-level query parameter, - * or else the "author_name" filter parameter (when querying by nicename): this is - * irregular as most parameter helper methods either set a top level parameter or a - * filter, not both. - * - * _Usage with the author nicename string is deprecated._ Query by author ID instead. - * If the "rest-filter" plugin is not installed, the name query will have no effect. - * - * @method author - * @chainable - * @param {String|Number} author The nicename or ID for a particular author - * @returns The request instance (for chaining) - */ -parameterMixins.author = function( author ) { - if ( author === undefined ) { - return this; - } - if ( typeof author === 'string' ) { - this.param( 'author', null ); - return filter.call( this, 'author_name', author ); - } - if ( typeof author === 'number' ) { - filter.call( this, 'author_name', null ); - return this.param( 'author', author ); - } - if ( author === null ) { - filter.call( this, 'author_name', null ); - return this.param( 'author', null ); - } - throw new Error( 'author must be either a nicename string or numeric ID' ); -}; - -/** - * Search for hierarchical taxonomy terms that are children of the parent term - * indicated by the provided term ID - * - * @example - * - * wp.pages().parent( 3 ).then(function( pages ) { - * // console.log( 'all of these pages are nested below page ID#3:' ); - * // console.log( pages ); - * }); - * - * wp.categories().parent( 42 ).then(function( categories ) { - * console.log( 'all of these categories are sub-items of cat ID#42:' ); - * console.log( categories ); - * }); - * - * @method parent - * @chainable - * @param {Number} parentId The ID of a (hierarchical) taxonomy term - * @returns The request instance (for chaining) - */ -parameterMixins.parent = paramSetter( 'parent' ); - -/** - * Specify the post for which to retrieve terms (relevant for *e.g.* taxonomy - * and comment collection endpoints). - * - * @method post - * @chainable - * @param {String|Number} post The ID of the post for which to retrieve terms - * @returns The request instance (for chaining) - */ -parameterMixins.post = paramSetter( 'post' ); - -/** - * Specify the password to use to access the content of a password-protected post - * - * @method password - * @chainable - * @param {string} password A string password to access protected content within a post - * @returns The request instance (for chaining) - */ -parameterMixins.password = paramSetter( 'password' ); - -/** - * Specify for which post statuses to return posts in a response collection - * - * See https://codex.wordpress.org/Post_Status -- the default post status - * values in WordPress which are most relevant to the API are 'publish', - * 'future', 'draft', 'pending', 'private', and 'trash'. This parameter also - * supports passing the special value "any" to return all statuses. - * - * @method status - * @chainable - * @param {string|string[]} status A status name string or array of strings - * @returns The request instance (for chaining) - */ -parameterMixins.status = paramSetter( 'status' ); - -/** - * Specify whether to return only, or to completely exclude, sticky posts - * - * @method sticky - * @chainable - * @param {boolean} sticky A boolean value for whether ONLY sticky posts (true) or - * NO sticky posts (false) should be returned in the query - * @returns The request instance (for chaining) - */ -parameterMixins.sticky = paramSetter( 'sticky' ); - -// Taxonomy Term Filter Methods -// ============================ - -/** - * Retrieve only records associated with one of the provided categories - * - * @method categories - * @chainable - * @param {String|Number|Array} categories A term ID integer or numeric string, or array thereof - * @returns The request instance (for chaining) - */ -parameterMixins.categories = paramSetter( 'categories' ); - -/** - * Legacy wrapper for `.categories()` that uses `?filter` to query by slug if available - * - * @method tag - * @deprecated Use `.categories()` and query by category IDs - * @chainable - * @param {String|Number|Array} tag A category term slug string, numeric ID, or array of numeric IDs - * @returns The request instance (for chaining) - */ -parameterMixins.category = function( category ) { - if ( argumentIsNumeric( category ) ) { - return parameterMixins.categories.call( this, category ); - } - return taxonomy.call( this, 'category', category ); -}; - -/** - * Exclude records associated with any of the provided category IDs - * - * @method excludeCategories - * @chainable - * @param {String|Number|Array} category A term ID integer or numeric string, or array thereof - * @returns The request instance (for chaining) - */ -parameterMixins.excludeCategories = paramSetter( 'categories_exclude' ); - -/** - * Retrieve only records associated with one of the provided tag IDs - * - * @method tags - * @chainable - * @param {String|Number|Array} tags A term ID integer or numeric string, or array thereof - * @returns The request instance (for chaining) - */ -parameterMixins.tags = paramSetter( 'tags' ); - -/** - * Legacy wrapper for `.tags()` that uses `?filter` to query by slug if available - * - * @method tag - * @deprecated Use `.tags()` and query by term IDs - * @chainable - * @param {String|Number|Array} tag A tag term slug string, numeric ID, or array of numeric IDs - * @returns The request instance (for chaining) - */ -parameterMixins.tag = function( tag ) { - if ( argumentIsNumeric( tag ) ) { - return parameterMixins.tags.call( this, tag ); - } - return taxonomy.call( this, 'tag', tag ); -}; - -/** - * Exclude records associated with any of the provided tag IDs - * - * @method excludeTags - * @chainable - * @param {String|Number|Array} category A term ID integer or numeric string, or array thereof - * @returns The request instance (for chaining) - */ -parameterMixins.excludeTags = paramSetter( 'tags_exclude' ); - -// Date Methods -// ============ - -/** - * Retrieve only records published before a specified date - * - * @example Provide an ISO 8601-compliant date string - * - * wp.posts().before('2016-03-22')... - * - * @example Provide a JavaScript Date object - * - * wp.posts().before( new Date( 2016, 03, 22 ) )... - * - * @method before - * @chainable - * @param {String|Date} date An ISO 8601-compliant date string, or Date object - * @returns The request instance (for chaining) - */ -parameterMixins.before = function( date ) { - return this.param( 'before', new Date( date ).toISOString() ); -}; - -/** - * Retrieve only records published after a specified date - * - * @example Provide an ISO 8601-compliant date string - * - * wp.posts().after('1986-03-22')... - * - * @example Provide a JavaScript Date object - * - * wp.posts().after( new Date( 1986, 03, 22 ) )... - * - * @method after - * @chainable - * @param {String|Date} date An ISO 8601-compliant date string, or Date object - * @returns The request instance (for chaining) - */ -parameterMixins.after = function( date ) { - return this.param( 'after', new Date( date ).toISOString() ); -}; - -module.exports = parameterMixins; diff --git a/lib/pagination.js b/lib/pagination.js deleted file mode 100644 index 6d1a5db1..00000000 --- a/lib/pagination.js +++ /dev/null @@ -1,87 +0,0 @@ -const parseLinkHeader = require( 'li' ).parse; - -const WPRequest = require( '../lib/constructors/wp-request' ); - -/** - * If the response is not paged, return the body as-is. If pagination - * information is present in the response headers, parse those headers into - * a custom `_paging` property on the response body. `_paging` contains links - * to the previous and next pages in the collection, as well as metadata - * about the size and number of pages in the collection. - * - * The structure of the `_paging` property is as follows: - * - * - `total` {Integer} The total number of records in the collection - * - `totalPages` {Integer} The number of pages available - * - `links` {Object} The parsed "links" headers, separated into individual URI strings - * - `next` {WPRequest} A WPRequest object bound to the "next" page (if page exists) - * - `prev` {WPRequest} A WPRequest object bound to the "previous" page (if page exists) - * - * @private - * @param {Object} result The response object from the HTTP request - * @param {Object} options The options hash from the original request - * @param {String} options.endpoint The base URL of the requested API endpoint - * @param {Object} httpTransport The HTTP transport object used by the original request - * @returns {Object} The pagination metadata object for this HTTP request, or else null - */ -function createPaginationObject( result, options, httpTransport ) { - let _paging = null; - - if ( ! result.headers ) { - // No headers: return as-is - return _paging; - } - - // Guard against capitalization inconsistencies in returned headers - Object.keys( result.headers ).forEach( ( header ) => { - result.headers[ header.toLowerCase() ] = result.headers[ header ]; - } ); - - if ( ! result.headers[ 'x-wp-totalpages' ] ) { - // No paging: return as-is - return _paging; - } - - const totalPages = +result.headers[ 'x-wp-totalpages' ]; - - if ( ! totalPages || totalPages === 0 ) { - // No paging: return as-is - return _paging; - } - - // Decode the link header object - const links = result.headers.link ? - parseLinkHeader( result.headers.link ) : - {}; - - // Store pagination data from response headers on the response collection - _paging = { - total: +result.headers[ 'x-wp-total' ], - totalPages: totalPages, - links: links, - }; - - // Create a WPRequest instance pre-bound to the "next" page, if available - if ( links.next ) { - _paging.next = new WPRequest( { - ...options, - transport: httpTransport, - endpoint: links.next, - } ); - } - - // Create a WPRequest instance pre-bound to the "prev" page, if available - if ( links.prev ) { - _paging.prev = new WPRequest( { - ...options, - transport: httpTransport, - endpoint: links.prev, - } ); - } - - return _paging; -} - -module.exports = { - createPaginationObject, -}; diff --git a/lib/path-part-setter.js b/lib/path-part-setter.js deleted file mode 100644 index 497d0813..00000000 --- a/lib/path-part-setter.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @module path-part-setter - */ -'use strict'; - -/** - * Return a function to set part of the request URL path. - * - * Path part setter methods may be either dynamic (*i.e.* may represent a - * "named group") or non-dynamic (representing a static part of the URL, which - * is usually a collection endpoint of some sort). Which type of function is - * returned depends on whether a given route has one or many sub-resources. - * - * @alias module:lib/path-part-setter.create - * @param {Object} node An object representing a level of an endpoint path hierarchy - * @returns {Function} A path part setter function - */ -function createPathPartSetter( node ) { - // Local references to `node` properties used by returned functions - const nodeLevel = node.level; - const nodeName = node.names[ 0 ]; - const supportedMethods = node.methods || []; - const dynamicChildren = node.children ? - Object.keys( node.children ) - .map( key => node.children[ key ] ) - .filter( childNode => ( childNode.namedGroup === true ) ) : - []; - const dynamicChild = dynamicChildren.length === 1 && dynamicChildren[ 0 ]; - const dynamicChildLevel = dynamicChild && dynamicChild.level; - - if ( node.namedGroup ) { - /** - * Set a dymanic (named-group) path part of a query URL. - * - * @example - * - * // id() is a dynamic path part setter: - * wp.posts().id( 7 ); // Get posts/7 - * - * @chainable - * @param {String|Number} val The path part value to set - * @returns {Object} The handler instance (for chaining) - */ - return function( val ) { - this.setPathPart( nodeLevel, val ); - if ( supportedMethods.length ) { - this._supportedMethods = supportedMethods; - } - return this; - }; - } else { - /** - * Set a non-dymanic (non-named-group) path part of a query URL, and - * set the value of a subresource if an input value is provided and - * exactly one named-group child node exists. - * - * @example - * - * // revisions() is a non-dynamic path part setter: - * wp.posts().id( 4 ).revisions(); // Get posts/4/revisions - * wp.posts().id( 4 ).revisions( 1372 ); // Get posts/4/revisions/1372 - * - * @chainable - * @param {String|Number} [val] The path part value to set (if provided) - * for a subresource within this resource - * @returns {Object} The handler instance (for chaining) - */ - return function( val ) { - // If the path part is not a namedGroup, it should have exactly one - // entry in the names array: use that as the value for this setter, - // as it will usually correspond to a collection endpoint. - this.setPathPart( nodeLevel, nodeName ); - - // If this node has exactly one dynamic child, this method may act as - // a setter for that child node. `dynamicChildLevel` will be falsy if the - // node does not have a child or has multiple children. - if ( val !== undefined && dynamicChildLevel ) { - this.setPathPart( dynamicChildLevel, val ); - } - return this; - }; - } -} - -module.exports = { - create: createPathPartSetter, -}; diff --git a/lib/resource-handler-spec.js b/lib/resource-handler-spec.js deleted file mode 100644 index 27201424..00000000 --- a/lib/resource-handler-spec.js +++ /dev/null @@ -1,126 +0,0 @@ -/** - * @module resource-handler-spec - */ -'use strict'; - -const createPathPartSetter = require( './path-part-setter' ).create; - -/** @private */ -function addLevelOption( levelsObj, level, obj ) { - levelsObj[ level ] = levelsObj[ level ] || []; - levelsObj[ level ].push( obj ); -} - -/** - * Assign a setter function for the provided node to the provided route - * handler object setters dictionary (mutates handler by reference). - * - * @private - * @param {Object} handler A route handler definition object - * @param {Object} node A route hierarchy level node object - */ -function assignSetterFnForNode( handler, node ) { - let setterFn; - - // For each node, add its handler to the relevant "level" representation - addLevelOption( handler._levels, node.level, { - component: node.component, - validate: node.validate, - methods: node.methods, - } ); - - // First level is set implicitly, no dedicated setter needed - if ( node.level > 0 ) { - - setterFn = createPathPartSetter( node ); - - node.names.forEach( ( name ) => { - // Convert from snake_case to camelCase - const setterFnName = name.replace( - /[_-]+\w/g, - match => match.replace( /[_-]+/, '' ).toUpperCase() - ); - - // Don't overwrite previously-set methods - if ( ! handler._setters[ setterFnName ] ) { - handler._setters[ setterFnName ] = setterFn; - } - } ); - } -} - -/** - * Walk the tree of a specific resource node to create the setter methods - * - * The API we want to produce from the node tree looks like this: - * - * wp.posts(); /wp/v2/posts - * wp.posts().id( 7 ); /wp/v2/posts/7 - * wp.posts().id( 7 ).revisions(); /wp/v2/posts/7/revisions - * wp.posts().id( 7 ).revisions( 8 ); /wp/v2/posts/7/revisions/8 - * - * ^ That last one's the tricky one: we can deduce that this parameter is "id", but - * that param will already be taken by the post ID, so sub-collections have to be - * set up as `.revisions()` to get the collection, and `.revisions( id )` to get a - * specific resource. - * - * @private - * @param {Object} node A node object - * @param {Object} [node.children] An object of child nodes - * // @returns {isLeaf} A boolean indicating whether the processed node is a leaf - */ -function extractSetterFromNode( handler, node ) { - - assignSetterFnForNode( handler, node ); - - if ( node.children ) { - // Recurse down to this node's children - Object.keys( node.children ).forEach( ( key ) => { - extractSetterFromNode( handler, node.children[ key ] ); - } ); - } -} - -/** - * Create a node handler specification object from a route definition object - * - * @name create - * @param {object} routeDefinition A route definition object - * @param {string} resource The string key of the resource for which to create a handler - * @returns {object} A handler spec object with _path, _levels and _setters properties - */ -function createNodeHandlerSpec( routeDefinition, resource ) { - - const handler = { - // A "path" is an ordered (by key) set of values composed into the final URL - _path: { - '0': resource, - }, - - // A "level" is a level-keyed object representing the valid options for - // one level of the resource URL - _levels: {}, - - // Objects that hold methods and properties which will be copied to - // instances of this endpoint's handler - _setters: {}, - - // Arguments (query parameters) that may be set in GET requests to endpoints - // nested within this resource route tree, used to determine the mixins to - // add to the request handler - _getArgs: routeDefinition._getArgs, - }; - - // Walk the tree - Object.keys( routeDefinition ).forEach( ( routeDefProp ) => { - if ( routeDefProp !== '_getArgs' ) { - extractSetterFromNode( handler, routeDefinition[ routeDefProp ] ); - } - } ); - - return handler; -} - -module.exports = { - create: createNodeHandlerSpec, -}; diff --git a/lib/route-tree.js b/lib/route-tree.js deleted file mode 100644 index 3ffc90d6..00000000 --- a/lib/route-tree.js +++ /dev/null @@ -1,205 +0,0 @@ -/** - * @module route-tree - */ -'use strict'; - -const namedGroupRE = require( './util/named-group-regexp' ).namedGroupRE; -const splitPath = require( './util/split-path' ); -const ensure = require( './util/ensure' ); -const objectReduce = require( './util/object-reduce' ); - -/** - * Method to use when reducing route components array. - * - * @private - * @param {object} routeObj A route definition object (set via .bind partial application) - * @param {object} topLevel The top-level route tree object for this set of routes (set - * via .bind partial application) - * @param {object} parentLevel The memo object, which is mutated as the reducer adds - * a new level handler for each level in the route - * @param {string} component The string defining this route component - * @param {number} idx The index of this component within the components array - * @param {string[]} components The array of all components - * @returns {object} The child object of the level being reduced - */ -function reduceRouteComponents( routeObj, topLevel, parentLevel, component, idx, components ) { - // Check to see if this component is a dynamic URL segment (i.e. defined by - // a named capture group regular expression). namedGroup will be `null` if - // the regexp does not match, or else an array defining the RegExp match, e.g. - // [ - // 'P[\\d]+)', - // 'id', // Name of the group - // '[\\d]+', // regular expression for this URL segment's contents - // index: 15, - // input: '/wp/v2/posts/(?P[\\d]+)' - // ] - const namedGroup = component.match( namedGroupRE ); - // Pull out references to the relevant indices of the match, for utility: - // `null` checking is necessary in case the component did not match the RE, - // hence the `namedGroup &&`. - const groupName = namedGroup && namedGroup[ 1 ]; - const groupPattern = namedGroup && namedGroup[ 2 ]; - - // When branching based on a dynamic capture group we used the group's RE - // pattern as the unique identifier: this is done because the same group - // could be assigned different names in different endpoint handlers, e.g. - // "id" for posts/:id vs "parent_id" for posts/:parent_id/revisions. - // - // There is an edge case where groupPattern will be "" if we are registering - // a custom route via `.registerRoute` that does not include parameter - // validation. In this case we assume the groupName is sufficiently unique, - // and fall back to `|| groupName` for the levelKey string. - const levelKey = namedGroup ? ( groupPattern || groupName ) : component; - - // Level name on the other hand takes its value from the group's name, if - // defined, and falls back to the component string to handle situations where - // `component` is a collection (e.g. "revisions") - const levelName = namedGroup ? groupName : component; - - // Check whether we have a preexisting node at this level of the tree, and - // create a new level object if not. The component string is included so that - // validators can throw meaningful errors as appropriate. - const currentLevel = parentLevel[ levelKey ] || { - component: component, - namedGroup: namedGroup ? true : false, - level: idx, - names: [], - }; - - // A level's "names" correspond to the list of strings which could describe - // an endpoint's component setter functions: "id", "revisions", etc. - if ( currentLevel.names.indexOf( levelName ) < 0 ) { - currentLevel.names.push( levelName ); - } - - // A level's validate method is called to check whether a value being set - // on the request URL is of the proper type for the location in which it - // is specified. If a group pattern was found, the validator checks whether - // the input string exactly matches the group pattern. - const groupPatternRE = groupPattern === '' ? - // If groupPattern is an empty string, accept any input without validation - /.*/ : - // Otherwise, validate against the group pattern or the component string - new RegExp( groupPattern ? '^' + groupPattern + '$' : component, 'i' ); - - // Only one validate function is maintained for each node, because each node - // is defined either by a string literal or by a specific regular expression. - currentLevel.validate = input => groupPatternRE.test( input ); - - // Check to see whether to expect more nodes within this branch of the tree, - if ( components[ idx + 1 ] ) { - // and create a "children" object to hold those nodes if necessary - currentLevel.children = currentLevel.children || {}; - } else { - // At leaf nodes, specify the method capabilities of this endpoint - currentLevel.methods = ( routeObj.methods || [] ).map( str => str.toLowerCase() ); - - // Ensure HEAD is included whenever GET is supported: the API automatically - // adds support for HEAD if you have GET - if ( currentLevel.methods.indexOf( 'get' ) > -1 && currentLevel.methods.indexOf( 'head' ) === -1 ) { - currentLevel.methods.push( 'head' ); - } - - // At leaf nodes also flag (at the top level) what arguments are - // available to GET requests, so that we may automatically apply the - // appropriate parameter mixins - if ( routeObj.endpoints ) { - topLevel._getArgs = topLevel._getArgs || {}; - routeObj.endpoints.forEach( ( endpoint ) => { - // `endpoint.methods` will be an array of methods like `[ 'GET' ]`: we - // only care about GET for this exercise. Validating POST and PUT args - // could be useful but is currently deemed to be out-of-scope. - endpoint.methods.forEach( ( method ) => { - if ( method.toLowerCase() === 'get' ) { - Object.keys( endpoint.args ).forEach( ( argKey ) => { - // Reference param definition objects in the top _getArgs dictionary - topLevel._getArgs[ argKey ] = endpoint.args[ argKey ]; - } ); - } - } ); - } ); - } - } - - // Return the child node object as the new "level" - parentLevel[ levelKey ] = currentLevel; - return currentLevel.children; -} - -/** - * - * @private - * @param {object} namespaces The memo object that becomes a dictionary mapping API - * namespaces to an object of the namespace's routes - * @param {object} routeObj A route definition object - * @param {string} route The string key of the `routeObj` route object - * @returns {object} The namespaces dictionary memo object - */ -function reduceRouteTree( namespaces, routeObj, route ) { - const nsForRoute = routeObj.namespace; - - const routeString = route - // Strip the namespace from the route string (all routes should have the - // format `/namespace/other/stuff`) @TODO: Validate this assumption - .replace( '/' + nsForRoute + '/', '' ) - // Also strip any trailing "/?": the slash is already optional and a single - // question mark would break the regex parser - .replace( /\/\?$/, '' ); - - // Split the routes up into hierarchical route components - const routeComponents = splitPath( routeString ); - - // Do not make a namespace group for the API root - // Do not add the namespace root to its own group - // Do not take any action if routeString is empty - if ( ! nsForRoute || '/' + nsForRoute === route || ! routeString ) { - return namespaces; - } - - // Ensure that the namespace object for this namespace exists - ensure( namespaces, nsForRoute, {} ); - - // Get a local reference to namespace object - const ns = namespaces[ nsForRoute ]; - - // The first element of the route tells us what type of resource this route - // is for, e.g. "posts" or "comments": we build one handler per resource - // type, so we group like resource paths together. - const resource = routeComponents[0]; - - // @TODO: This code above currently precludes baseless routes, e.g. - // myplugin/v2/(?P\w+) -- should those be supported? - - // Create an array to represent this resource, and ensure it is assigned - // to the namespace object. The array will structure the "levels" (path - // components and subresource types) of this resource's endpoint handler. - ensure( ns, resource, {} ); - const levels = ns[ resource ]; - - // Recurse through the route components, mutating levels with information about - // each child node encountered while walking through the routes tree and what - // arguments (parameters) are available for GET requests to this endpoint. - routeComponents.reduce( - reduceRouteComponents.bind( null, routeObj, levels ), - levels - ); - - return namespaces; -} - -/** - * Build a route tree by reducing over a routes definition object from the API - * root endpoint response object - * - * @method build - * @param {object} routes A dictionary of routes keyed by route regex strings - * @returns {object} A dictionary, keyed by namespace, of resource handler - * factory methods for each namespace's resources - */ -function buildRouteTree( routes ) { - return objectReduce( routes, reduceRouteTree, {} ); -} - -module.exports = { - build: buildRouteTree, -}; diff --git a/lib/util/alphanumeric-sort.js b/lib/util/alphanumeric-sort.js deleted file mode 100644 index b0ea5614..00000000 --- a/lib/util/alphanumeric-sort.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -/** - * Utility function for sorting arrays of numbers or strings. - * - * @module util/alphanumeric-sort - * @param {String|Number} a The first comparator operand - * @param {String|Number} a The second comparator operand - * @returns -1 if the values are backwards, 1 if they're ordered, and 0 if they're the same - */ -module.exports = ( a, b ) => { - if ( a > b ) { - return 1; - } - if ( a < b ) { - return -1; - } - return 0; -}; diff --git a/lib/util/apply-mixin.js b/lib/util/apply-mixin.js deleted file mode 100644 index 9bfc387b..00000000 --- a/lib/util/apply-mixin.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -/** - * Augment an object (specifically a prototype) with a mixin method - * (the provided object is mutated by reference) - * - * @module util/apply-mixin - * @param {Object} obj The object (usually a prototype) to augment - * @param {String} key The property to which the mixin method should be assigned - * @param {Function} mixin The mixin method - * @returns {void} - */ -module.exports = ( obj, key, mixin ) => { - // Will not overwrite existing methods - if ( typeof mixin === 'function' && ! obj[ key ] ) { - obj[ key ] = mixin; - } -}; diff --git a/lib/util/argument-is-numeric.js b/lib/util/argument-is-numeric.js deleted file mode 100644 index 5e17f5af..00000000 --- a/lib/util/argument-is-numeric.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -/** - * Return true if the provided argument is a number, a numeric string, or an - * array of numbers or numeric strings - * - * @module util/argument-is-numeric - * @param {Number|String|Number[]|String[]} val The value to inspect - * @param {String} key The property to which the mixin method should be assigned - * @param {Function} mixin The mixin method - * @returns {void} - */ -const argumentIsNumeric = ( val ) => { - if ( typeof val === 'number' ) { - return true; - } - - if ( typeof val === 'string' ) { - return /^\d+$/.test( val ); - } - - if ( Array.isArray( val ) ) { - for ( let i = 0; i < val.length; i++ ) { - // Fail early if any argument isn't determined to be numeric - if ( ! argumentIsNumeric( val[ i ] ) ) { - return false; - } - } - return true; - } - - // If it's not an array, and not a string, and not a number, we don't - // know what to do with it - return false; -}; - -module.exports = argumentIsNumeric; diff --git a/lib/util/check-method-support.js b/lib/util/check-method-support.js deleted file mode 100644 index 5223fb93..00000000 --- a/lib/util/check-method-support.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -/** - * Verify that a specific HTTP method is supported by the provided WPRequest - * - * @module util/check-method-support - * @param {String} method An HTTP method to check ('get', 'post', etc) - * @param {WPRequest} request A WPRequest object with a _supportedMethods array - * @returns true iff the method is within request._supportedMethods - */ -module.exports = ( method, request ) => { - if ( request._supportedMethods.indexOf( method.toLowerCase() ) === -1 ) { - throw new Error( - 'Unsupported method; supported methods are: ' + - request._supportedMethods.join( ', ' ) - ); - } - - return true; -}; diff --git a/lib/util/ensure.js b/lib/util/ensure.js deleted file mode 100644 index 46e08f43..00000000 --- a/lib/util/ensure.js +++ /dev/null @@ -1,17 +0,0 @@ -'use strict'; - -/** - * Ensure that a property is present in an object, initializing it to a default - * value if it is not already defined. Modifies the provided object by reference. - * - * @module util/ensure - * @param {object} obj The object in which to ensure a property exists - * @param {string} prop The property key to ensure - * @param {} propDefaultValue The default value for the property - * @returns {void} - */ -module.exports = ( obj, prop, propDefaultValue ) => { - if ( obj && obj[ prop ] === undefined ) { - obj[ prop ] = propDefaultValue; - } -}; diff --git a/lib/util/is-empty-object.js b/lib/util/is-empty-object.js deleted file mode 100644 index fcd96463..00000000 --- a/lib/util/is-empty-object.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -/** - * Determine whether an provided value is an empty object - * - * @module util/is-empty-object - * @param {} value A value to test for empty-object-ness - * @returns {boolean} Whether the provided value is an empty object - */ -module.exports = ( value ) => { - // If the value is not object-like, then it is certainly not an empty object - if ( typeof value !== 'object' ) { - return false; - } - - // For our purposes an empty array should not be treated as an empty object - // (Since this is used to process invalid content-type responses, ) - if ( Array.isArray( value ) ) { - return false; - } - - for ( const key in value ) { - if ( value.hasOwnProperty( key ) ) { - return false; - } - } - - return true; -}; diff --git a/lib/util/key-val-to-obj.js b/lib/util/key-val-to-obj.js deleted file mode 100644 index 10d185f2..00000000 --- a/lib/util/key-val-to-obj.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -/** - * Convert a (key, value) pair to a { key: value } object - * - * @module util/key-val-to-obj - * @param {string} key The key to use in the returned object - * @param {} value The value to assign to the provided key - * @returns {object} A dictionary object containing the key-value pair - */ -module.exports = ( key, value ) => { - const obj = {}; - obj[ key ] = value; - return obj; -}; diff --git a/lib/util/log-obj.js b/lib/util/log-obj.js deleted file mode 100644 index 0b856bee..00000000 --- a/lib/util/log-obj.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -const inspect = require( 'util' ).inspect; - -/** - * Helper method for debugging only: use util.inspect to log a full object - * - * @module util/log-obj - * @private - * @param {object} obj The object to log - * @returns {void} - */ -module.exports = ( obj ) => { - // eslint-disable-next-line no-console - console.log( inspect( obj, { - colors: true, - depth: null, - } ) ); -}; diff --git a/lib/util/named-group-regexp.js b/lib/util/named-group-regexp.js deleted file mode 100644 index 3eaeb787..00000000 --- a/lib/util/named-group-regexp.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @module util/named-group-regexp - */ -'use strict'; - -const pattern = [ - // Capture group start - '\\(\\?', - // Capture group name begins either `P<`, `<` or `'` - '(?:P<|<|\')', - // Everything up to the next `>`` or `'` (depending) will be the capture group name - '([^>\']+)', - // Capture group end - '[>\']', - // Get everything up to the end of the capture group: this is the RegExp used - // when matching URLs to this route, which we can use for validation purposes. - '([^\\)]*(\\))?)\\??', - // Capture group end - '\\)', -].join( '' ); - -module.exports = { - /** - * String representation of the exported Regular Expression; we construct this - * RegExp from a string to enable more detailed annotation and permutation - * - * @prop {String} pattern - */ - pattern: pattern, - - /** - * Regular Expression to identify a capture group in PCRE formats - * `(?regex)`, `(?'name'regex)` or `(?Pregex)` (see - * regular-expressions.info/refext.html) - * - * @prop {RegExp} namedGroupRE - */ - namedGroupRE: new RegExp( pattern ), -}; diff --git a/lib/util/object-reduce.js b/lib/util/object-reduce.js deleted file mode 100644 index a6f7a0d7..00000000 --- a/lib/util/object-reduce.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -/** - * Utility method to permit Array#reduce-like operations over objects - * - * This is likely to be slightly more inefficient than using lodash.reduce, - * but results in ~50kb less size in the resulting bundled code before - * minification and ~12kb of savings with minification. - * - * Unlike lodash.reduce(), the iterator and initial value properties are NOT - * optional: this is done to simplify the code. This module is not intended to - * be a full replacement for lodash.reduce and instead prioritizes simplicity - * for a specific common case. - * - * @module util/object-reduce - * @private - * @param {Object} obj An object of key-value pairs - * @param {Function} iterator A function to use to reduce the object - * @param {*} initialState The initial value to pass to the reducer function - * @returns The result of the reduction operation - */ -module.exports = ( obj, iterator, initialState ) => Object - .keys( obj ) - .reduce( - ( memo, key ) => iterator( memo, obj[ key ], key ), - initialState - ); diff --git a/lib/util/parameter-setter.js b/lib/util/parameter-setter.js deleted file mode 100644 index 2dd8a57a..00000000 --- a/lib/util/parameter-setter.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -/** - * Helper to create a simple parameter setter convenience method - * - * @module util/parameter-setter - * @param {String} param The string key of the parameter this method will set - * @returns {Function} A setter method that can be assigned to a request instance - */ -module.exports = ( param ) => { - /** - * A setter for a specific parameter - * - * @chainable - * @param {*} val The value to set for the the parameter - * @returns The request instance on which this method was called (for chaining) - */ - return function( val ) { - return this.param( param, val ); - }; -}; diff --git a/lib/util/split-path.js b/lib/util/split-path.js deleted file mode 100644 index 42fcf3a8..00000000 --- a/lib/util/split-path.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @module util/split-path - */ -'use strict'; - -const namedGroupPattern = require( './named-group-regexp' ).pattern; - -// Convert capture groups to non-matching groups, because all capture groups -// are included in the resulting array when an RE is passed to `.split()` -// (We re-use the existing named group's capture pattern instead of creating -// a new RegExp just for this purpose) -const patternWithoutSubgroups = namedGroupPattern - .replace( /([^\\])\(([^?])/g, '$1(?:$2' ); - -// Make a new RegExp using the same pattern as one single unified capture group, -// so the match as a whole will be preserved after `.split()`. Permit non-slash -// characters before or after the named capture group, although those components -// will not yield functioning setters. -const namedGroupRE = new RegExp( '([^/]*' + patternWithoutSubgroups + '[^/]*)' ); - -/** - * Divide a route string up into hierarchical components by breaking it apart - * on forward slash characters. - * - * There are plugins (including Jetpack) that register routes with regex capture - * groups which also contain forward slashes, so those groups have to be pulled - * out first before the remainder of the string can be .split() as normal. - * - * @param {String} pathStr A route path string to break into components - * @returns {String[]} An array of route component strings - */ -module.exports = pathStr => pathStr - // Divide a string like "/some/path/(?P)/etc" into an - // array `[ "/some/path/", "(?P)", "/etc" ]`. - .split( namedGroupRE ) - // Then, reduce through the array of parts, splitting any non-capture-group - // parts on forward slashes and discarding empty strings to create the final - // array of path components. - .reduce( ( components, part ) => { - if ( ! part ) { - // Ignore empty strings parts - return components; - } - - if ( namedGroupRE.test( part ) ) { - // Include named capture groups as-is - return components.concat( part ); - } - - // Split the part on / and filter out empty strings - return components.concat( part.split( '/' ).filter( Boolean ) ); - }, [] ); diff --git a/lib/util/unique.js b/lib/util/unique.js deleted file mode 100644 index 886c0c7d..00000000 --- a/lib/util/unique.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Return an array with all duplicate items removed. - * - * This functionality was previously provided by lodash.uniq, but this - * modern JS solution yields a smaller bundle size. - * - * @param {Array} arr An array to de-duplicate - * @returns {Array} A de-duplicated array - */ -module.exports = arr => Array.from( new Set( arr ) ); diff --git a/lib/wp-register-route.js b/lib/wp-register-route.js deleted file mode 100644 index 7a27497f..00000000 --- a/lib/wp-register-route.js +++ /dev/null @@ -1,102 +0,0 @@ -'use strict'; - -const buildRouteTree = require( './route-tree' ).build; -const generateEndpointFactories = require( './endpoint-factories' ).generate; -const paramSetter = require( './util/parameter-setter' ); -const applyMixin = require( './util/apply-mixin' ); -const mixins = require( './mixins' ); - -/** - * Create and return a handler for an arbitrary WP REST API endpoint. - * - * The first two parameters mirror `register_rest_route` in the REST API - * codebase: - * - * @memberof! WPAPI# - * @param {string} namespace A namespace string, e.g. 'myplugin/v1' - * @param {string} restBase A REST route string, e.g. '/author/(?P\d+)' - * @param {object} [options] An (optional) options object - * @param {object} [options.mixins] A hash of functions to apply as mixins - * @param {string[]} [options.methods] An array of methods to whitelist (on the leaf node only) - * @returns {Function} An endpoint handler factory function for the specified route - */ -function registerRoute( namespace, restBase, options = {} ) { - // Support all methods until requested to do otherwise - let supportedMethods = [ 'head', 'get', 'patch', 'put', 'post', 'delete' ]; - - if ( Array.isArray( options.methods ) ) { - // Permit supported methods to be specified as an array - supportedMethods = options.methods.map( method => method.trim().toLowerCase() ); - } else if ( typeof options.methods === 'string' ) { - // Permit a supported method to be specified as a string - supportedMethods = [ options.methods.trim().toLowerCase() ]; - } - - // Ensure that if GET is supported, then HEAD is as well, and vice-versa - if ( supportedMethods.indexOf( 'get' ) !== -1 && supportedMethods.indexOf( 'head' ) === -1 ) { - supportedMethods.push( 'head' ); - } else if ( supportedMethods.indexOf( 'head' ) !== -1 && supportedMethods.indexOf( 'get' ) === -1 ) { - supportedMethods.push( 'get' ); - } - - const fullRoute = namespace - // Route should always have preceding slash - .replace( /^[\s/]*/, '/' ) - // Route should always be joined to namespace with a single slash - .replace( /[\s/]*$/, '/' ) + restBase.replace( /^[\s/]*/, '' ); - - const routeObj = {}; - routeObj[ fullRoute ] = { - namespace: namespace, - methods: supportedMethods, - }; - - // Go through the same steps used to bootstrap the client to parse the - // provided route out into a handler request method - const routeTree = buildRouteTree( routeObj ); - // Parse the mock route object into endpoint factories - const endpointFactories = generateEndpointFactories( routeTree )[ namespace ]; - const EndpointRequest = endpointFactories[ Object.keys( endpointFactories )[ 0 ] ].Ctor; - - if ( options && options.params ) { - options.params.forEach( ( param ) => { - // Only accept string parameters - if ( typeof param !== 'string' ) { - return; - } - - // If the parameter can be mapped to a mixin, apply that mixin - if ( typeof mixins[ param ] === 'object' ) { - Object.keys( mixins[ param ] ).forEach( ( key ) => { - applyMixin( EndpointRequest.prototype, key, mixins[ param ][ key ] ); - } ); - return; - } - - // Attempt to create a simple setter for any parameters for which - // we do not already have a custom mixin - applyMixin( EndpointRequest.prototype, param, paramSetter( param ) ); - } ); - } - - // Set any explicitly-provided object mixins - if ( options && typeof options.mixins === 'object' ) { - - // Set any specified mixin functions on the response - Object.keys( options.mixins ).forEach( ( key ) => { - applyMixin( EndpointRequest.prototype, key, options.mixins[ key ] ); - } ); - } - - function endpointFactory( options = {} ) { - return new EndpointRequest( { - ...options, - ...( this ? this._options : {} ), - } ); - } - endpointFactory.Ctor = EndpointRequest; - - return endpointFactory; -} - -module.exports = registerRoute; diff --git a/LICENSE b/license.md similarity index 94% rename from LICENSE rename to license.md index 9ad9a394..c1672247 100644 --- a/LICENSE +++ b/license.md @@ -1,3 +1,9 @@ +--- +layout: page +title: License +permalink: /license/ +--- + The MIT License (MIT) Copyright (c) 2014 K.Adam White diff --git a/node-wpapi.php b/node-wpapi.php deleted file mode 100644 index 24eb81a2..00000000 --- a/node-wpapi.php +++ /dev/null @@ -1,61 +0,0 @@ - esc_url_raw( rest_url() ), - 'nonce' => wp_create_nonce( 'wp_rest' ), - ] - ); -} - -/** - * Read & parse a JSON file. - * - * @param string $path The path to the JSON file to load. - * @return array The parsed JSON data object. - */ -function read_json( string $path ) : array { - if ( ! file_exists( $path ) ) { - return []; - } - $contents = file_get_contents( $path ); - if ( empty( $contents ) ) { - return []; - } - return json_decode( $contents, true ); -} diff --git a/package.json b/package.json deleted file mode 100644 index 4df37586..00000000 --- a/package.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "author": { - "name": "K.Adam White", - "url": "http://www.kadamwhite.com" - }, - "name": "wpapi", - "version": "2.0.0-alpha.1", - "description": "An isomorphic JavaScript client for interacting with the WordPress REST API", - "main": "wpapi.js", - "repository": { - "type": "git", - "url": "https://github.com/wp-api/node-wpapi.git" - }, - "license": "MIT", - "bugs": { - "url": "https://github.com/wp-api/node-wpapi/issues" - }, - "homepage": "https://github.com/wp-api/node-wpapi", - "keywords": [ - "api", - "client", - "cms", - "wordpress" - ], - "browserslist": "last 2 versions, ie 11, not dead", - "jest": { - "testMatch": [ - "**/__tests__/**/*.js", - "**/?(*.)+(spec|test).js", - "**/tests/**/*.js" - ], - "testPathIgnorePatterns": [ - "test.js", - ".eslintrc.js", - "/tests/helpers/" - ], - "coveragePathIgnorePatterns": [ - "/tests/helpers/" - ] - }, - "scripts": { - "build": "webpack && webpack --config webpack.config.minified.js", - "build:stats": "webpack && webpack --config webpack.config.minified.js --stats", - "zip": "npm run build && grunt zip", - "update-default-routes-json": "node build/scripts/update-default-routes-json", - "predocs": "rimraf documentation/api-reference", - "docs": "grunt docs && jsdoc -c .jsdoc.json --verbose", - "postdocs": "rimraf documentation/api-readme.md", - "jekyll": "node bin/jekyll", - "release-docs": "node build/scripts/release-docs", - "release-npm": "npm run build && npm test && npm publish", - "eslint": "eslint Gruntfile.js wpapi.js bin build lib tests", - "lint": "npm run eslint", - "watch": "jest --watch", - "test:all": "jest", - "test:unit": "jest tests/unit superagent/tests/unit fetch/tests/unit", - "test:integration": "jest tests/integration", - "test:ci": "npm run eslint && jest tests/unit --coverage", - "pretest": "npm run lint || true", - "test": "jest --coverage" - }, - "dependencies": { - "li": "^1.3.0", - "parse-link-header": "^1.0.1", - "qs": "^6.7.0" - }, - "optionalDependencies": { - "form-data": "^2.5.0", - "node-fetch": "^2.6.0", - "superagent": "^4.1.0" - }, - "devDependencies": { - "@babel/core": "^7.5.0", - "@babel/plugin-proposal-object-rest-spread": "^7.2.0", - "@babel/preset-env": "^7.5.0", - "babel-loader": "^8.0.6", - "combyne": "^2.0.0", - "eslint": "^4.19.1", - "grunt": "^1.0.4", - "grunt-cli": "^1.2.0", - "grunt-contrib-clean": "^1.0.0", - "grunt-zip": "^0.17.1", - "jest": "^24.8.0", - "jsdoc": "^3.6.2", - "kramed": "^0.5.6", - "load-grunt-tasks": "^3.5.0", - "minami": "^1.2.3", - "minimist": "^1.2.0", - "prompt": "^1.0.0", - "rimraf": "^2.6.3", - "webpack": "^4.35.0", - "webpack-bundle-analyzer": "^3.3.2", - "webpack-cli": "^3.3.5" - } -} diff --git a/superagent/README.md b/superagent/README.md deleted file mode 100644 index 532f647a..00000000 --- a/superagent/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# `wpapi/superagent` - -This endpoint returns a version of the WPAPI library configured to use Superagent for HTTP requests. - -## Installation & Usage - -Install both `wpapi` and `superagent` using the command `npm install --save wpapi superagent`. - -```js -import WPAPI from 'wpapi/superagent'; - -// Configure and use WPAPI as normal -const site = new WPAPI( { /* ... */ } ); -``` diff --git a/superagent/index.js b/superagent/index.js deleted file mode 100644 index 1e105cb3..00000000 --- a/superagent/index.js +++ /dev/null @@ -1,6 +0,0 @@ -const WPAPI = require( '../wpapi' ); -const superagentTransport = require( './superagent-transport' ); -const bindTransport = require( '../lib/bind-transport' ); - -// Bind the superagent-based HTTP transport to the WPAPI constructor -module.exports = bindTransport( WPAPI, superagentTransport ); diff --git a/superagent/superagent-transport.js b/superagent/superagent-transport.js deleted file mode 100644 index 36057d02..00000000 --- a/superagent/superagent-transport.js +++ /dev/null @@ -1,269 +0,0 @@ -/** - * @module http-transport - */ -'use strict'; - -const agent = require( 'superagent' ); - -const checkMethodSupport = require( '../lib/util/check-method-support' ); -const objectReduce = require( '../lib/util/object-reduce' ); -const isEmptyObject = require( '../lib/util/is-empty-object' ); -const { createPaginationObject } = require( '../lib/pagination' ); - -/** - * Set any provided headers on the outgoing request object. Runs after _auth. - * - * @method _setHeaders - * @private - * @param {Object} request A superagent request object - * @param {Object} options A WPRequest _options object - * @param {Object} A superagent request object, with any available headers set - */ -function _setHeaders( request, options ) { - // If there's no headers, do nothing - if ( ! options.headers ) { - return request; - } - - return objectReduce( - options.headers, - ( request, value, key ) => request.set( key, value ), - request - ); -} - -/** - * Conditionally set basic authentication on a server request object. - * - * @method _auth - * @private - * @param {Object} request A superagent request object - * @param {Object} options A WPRequest _options object - * @param {Boolean} forceAuthentication whether to force authentication on the request - * @param {Object} A superagent request object, conditionally configured to use basic auth - */ -function _auth( request, options, forceAuthentication ) { - // If we're not supposed to authenticate, don't even start - if ( ! forceAuthentication && ! options.auth && ! options.nonce ) { - return request; - } - - // Enable nonce in options for Cookie authentication http://wp-api.org/guides/authentication.html - if ( options.nonce ) { - request.set( 'X-WP-Nonce', options.nonce ); - return request; - } - - // Retrieve the username & password from the request options if they weren't provided - const username = options.username; - const password = options.password; - - // If no username or no password, can't authenticate - if ( ! username || ! password ) { - return request; - } - - // Can authenticate: set basic auth parameters on the request - return request.auth( username, password ); -} - -// Pagination-Related Helpers -// ========================== - -/** - * Extract the body property from the superagent response, or else try to parse - * the response text to get a JSON object. - * - * @private - * @param {Object} response The response object from the HTTP request - * @param {String} response.text The response content as text - * @param {Object} response.body The response content as a JS object - * @returns {Object} The response content as a JS object - */ -function extractResponseBody( response ) { - let responseBody = response.body; - if ( isEmptyObject( responseBody ) && response.type === 'text/html' ) { - // Response may have come back as HTML due to caching plugin; try to parse - // the response text into JSON - try { - responseBody = JSON.parse( response.text ); - } catch ( e ) { - // Swallow errors, it's OK to fall back to returning the body - } - } - return responseBody; -} - -// HTTP-Related Helpers -// ==================== - -/** - * Submit the provided superagent request object and return a promise which - * resolves to the response from the HTTP request. - * - * @private - * @param {Object} request A superagent request object - * @param {Function} transform A function to transform the result data - * @returns {Promise} A promise to the superagent request - */ -function invokeAndPromisify( request, transform ) { - return new Promise( ( resolve, reject ) => { - // Fire off the result - request.end( ( err, result ) => { - - // Return the results as a promise - if ( err || result.error ) { - reject( err || result.error ); - } else { - resolve( result ); - } - } ); - } ).then( transform ).catch( ( err ) => { - // If the API provided an error object, it will be available within the - // superagent response object as response.body (containing the response - // JSON). If that object exists, it will have a .code property if it is - // truly an API error (non-API errors will not have a .code). - if ( err.response && err.response.body && err.response.body.code ) { - // Forward API error response JSON on to the calling method: omit - // all transport-specific (superagent-specific) properties - err = err.response.body; - } - // Re-throw the error so that it can be handled by a Promise .catch or .then - throw err; - } ); -} - -/** - * Return the body of the request, augmented with pagination information if the - * result is a paged collection. - * - * @private - * @param {WPRequest} wpreq The WPRequest representing the returned HTTP response - * @param {Object} result The results from the HTTP request - * @returns {Object} The "body" property of the result, conditionally augmented with - * pagination information if the result is a partial collection. - */ -function returnBody( wpreq, result ) { - const body = extractResponseBody( result ); - const _paging = createPaginationObject( result, wpreq._options, wpreq.transport ); - if ( _paging ) { - body._paging = _paging; - } - return body; -} - -/** - * Extract and return the headers property from a superagent response object - * - * @private - * @param {Object} result The results from the HTTP request - * @returns {Object} The "headers" property of the result - */ -function returnHeaders( result ) { - return result.headers; -} - -// HTTP Methods: Private HTTP-verb versions -// ======================================== - -/** - * @method get - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpGet( wpreq ) { - checkMethodSupport( 'get', wpreq ); - const url = wpreq.toString(); - - let request = _auth( agent.get( url ), wpreq._options ); - request = _setHeaders( request, wpreq._options ); - - return invokeAndPromisify( request, returnBody.bind( null, wpreq ) ); -} - -/** - * Invoke an HTTP "POST" request against the provided endpoint - * @method post - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @param {Object} data The data for the POST request - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpPost( wpreq, data ) { - checkMethodSupport( 'post', wpreq ); - const url = wpreq.toString(); - data = data || {}; - let request = _auth( agent.post( url ), wpreq._options, true ); - request = _setHeaders( request, wpreq._options ); - - if ( wpreq._attachment ) { - // Data must be form-encoded alongside image attachment - request = objectReduce( - data, - ( req, value, key ) => req.field( key, value ), - request.attach( 'file', wpreq._attachment, wpreq._attachmentName ) - ); - } else { - request = request.send( data ); - } - - return invokeAndPromisify( request, returnBody.bind( null, wpreq ) ); -} - -/** - * @method put - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @param {Object} data The data for the PUT request - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpPut( wpreq, data ) { - checkMethodSupport( 'put', wpreq ); - const url = wpreq.toString(); - data = data || {}; - - let request = _auth( agent.put( url ), wpreq._options, true ).send( data ); - request = _setHeaders( request, wpreq._options ); - - return invokeAndPromisify( request, returnBody.bind( null, wpreq ) ); -} - -/** - * @method delete - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @param {Object} [data] Data to send along with the DELETE request - * @returns {Promise} A promise to the results of the HTTP request - */ -function _httpDelete( wpreq, data ) { - checkMethodSupport( 'delete', wpreq ); - const url = wpreq.toString(); - let request = _auth( agent.del( url ), wpreq._options, true ).send( data ); - request = _setHeaders( request, wpreq._options ); - - return invokeAndPromisify( request, returnBody.bind( null, wpreq ) ); -} - -/** - * @method head - * @async - * @param {WPRequest} wpreq A WPRequest query object - * @returns {Promise} A promise to the header results of the HTTP request - */ -function _httpHead( wpreq ) { - checkMethodSupport( 'head', wpreq ); - const url = wpreq.toString(); - let request = _auth( agent.head( url ), wpreq._options ); - request = _setHeaders( request, wpreq._options ); - - return invokeAndPromisify( request, returnHeaders ); -} - -module.exports = { - delete: _httpDelete, - get: _httpGet, - head: _httpHead, - post: _httpPost, - put: _httpPut, -}; diff --git a/superagent/tests/.eslintrc.js b/superagent/tests/.eslintrc.js deleted file mode 100644 index bd527d76..00000000 --- a/superagent/tests/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - 'env': { - jest: true, - }, -}; diff --git a/superagent/tests/unit/superagent.js b/superagent/tests/unit/superagent.js deleted file mode 100644 index c7703ef3..00000000 --- a/superagent/tests/unit/superagent.js +++ /dev/null @@ -1,164 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../' ); - -// HTTP transport, for stubbing -const superagentTransport = require( '../../superagent-transport' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe( 'WPAPI', () => { - - describe( 'constructor', () => { - - describe( 'assigns default HTTP transport', () => { - - it( 'for GET requests', () => { - jest.spyOn( superagentTransport, 'get' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( '' ); - query.get(); - expect( superagentTransport.get ).toHaveBeenCalledWith( query ); - superagentTransport.get.mockRestore(); - } ); - - it( 'for POST requests', () => { - jest.spyOn( superagentTransport, 'post' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( '' ); - const data = {}; - query.create( data ); - expect( superagentTransport.post ).toHaveBeenCalledWith( query, data ); - superagentTransport.post.mockRestore(); - } ); - - it( 'for POST requests', () => { - jest.spyOn( superagentTransport, 'post' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( '' ); - const data = {}; - query.create( data ); - expect( superagentTransport.post ).toHaveBeenCalledWith( query, data ); - superagentTransport.post.mockRestore(); - } ); - - it( 'for PUT requests', () => { - jest.spyOn( superagentTransport, 'put' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( 'a-resource' ); - const data = {}; - query.update( data ); - expect( superagentTransport.put ).toHaveBeenCalledWith( query, data ); - superagentTransport.put.mockRestore(); - } ); - - it( 'for DELETE requests', () => { - jest.spyOn( superagentTransport, 'delete' ).mockImplementation( () => {} ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - const query = site.root( 'a-resource' ); - const data = { - force: true, - }; - query.delete( data ); - expect( superagentTransport.delete ).toHaveBeenCalledWith( query, data ); - superagentTransport.delete.mockRestore(); - } ); - - } ); - - } ); - - describe( '.transport constructor property', () => { - - it( 'is defined', () => { - expect( WPAPI ).toHaveProperty( 'transport' ); - } ); - - it( 'is an object', () => { - expect( typeof WPAPI.transport ).toBe( 'object' ); - } ); - - it( 'has methods for each http transport action', () => { - expect( typeof WPAPI.transport.delete ).toBe( 'function' ); - expect( typeof WPAPI.transport.get ).toBe( 'function' ); - expect( typeof WPAPI.transport.head ).toBe( 'function' ); - expect( typeof WPAPI.transport.post ).toBe( 'function' ); - expect( typeof WPAPI.transport.put ).toBe( 'function' ); - } ); - - it( 'is frozen (properties cannot be modified directly)', () => { - expect( () => { - WPAPI.transport.get = () => {}; - } ).toThrow(); - } ); - - } ); - - describe( '.discover() constructor method', () => { - - beforeEach( () => { - jest.spyOn( superagentTransport, 'get' ).mockImplementation( () => {} ); - } ); - - afterEach( () => { - superagentTransport.get.mockRestore(); - } ); - - it( 'is a function', () => { - expect( WPAPI ).toHaveProperty( 'discover' ); - expect( typeof WPAPI.discover ).toBe( 'function' ); - } ); - - it( 'discovers the API root with a GET request', () => { - const url = 'http://mozarts.house'; - superagentTransport.get.mockImplementation( () => Promise.resolve( { - name: 'Skip Beats', - descrition: 'Just another WordPress weblog', - routes: { - '/': { - _links: { - self: 'http://mozarts.house/wp-json/', - }, - }, - 'list': {}, - 'of': {}, - 'routes': {}, - }, - } ) ); - const prom = WPAPI.discover( url ) - .then( ( result ) => { - expect( result ).toBeInstanceOf( WPAPI ); - expect( result.root().toString() ).toBe( 'http://mozarts.house/wp-json/' ); - expect( superagentTransport.get ).toBeCalledTimes( 1 ); - const indexRequestObject = superagentTransport.get.mock.calls[0][0]; - expect( indexRequestObject.toString() ).toBe( 'http://mozarts.house/?rest_route=%2F' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'throws an error if no API endpoint can be discovered', () => { - const url = 'http://we.made.it/to/mozarts/house'; - superagentTransport.get.mockImplementationOnce( () => Promise.reject( 'Some error' ) ); - const prom = WPAPI.discover( url ) - .catch( ( err ) => { - expect( err ).toBe( 'Some error' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - -} ); diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js deleted file mode 100644 index bd527d76..00000000 --- a/tests/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - 'env': { - jest: true, - }, -}; diff --git a/tests/helpers/constants.js b/tests/helpers/constants.js deleted file mode 100644 index 50b60855..00000000 --- a/tests/helpers/constants.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -module.exports = { - credentials: { - username: 'admin', - password: 'password', - }, -}; diff --git a/tests/helpers/get-prop.js b/tests/helpers/get-prop.js deleted file mode 100644 index 91ce55e5..00000000 --- a/tests/helpers/get-prop.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -/** - * This is a curry-able pluck function, that is all. Inspecting a collection of - * human-readable strings is usually a more comprehensible way to validate the - * right results were returned than using arbitrary values. - * - * @example Pluck the slugs titles from a collection of terms - * - * const slugs = getProp( collection, 'slug' ); - * - * @example Create a bound variant that always plucks .name - * - * const getNames = getProp.bind( null, 'name' ); - * const names = getNames( collection ); - * - * @private - * @param {String} property The name of the property to pluck - * @param {Object[]} collection An array of response objects whence to pluck - * @returns {String[]} The values of that property from each collection member - */ -module.exports = ( property, collection ) => collection - .map( item => item[ property ] ); diff --git a/tests/helpers/get-rendered-prop.js b/tests/helpers/get-rendered-prop.js deleted file mode 100644 index 93b08921..00000000 --- a/tests/helpers/get-rendered-prop.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -/** - * This method is a super-pluck capable of grabbing the rendered version of - * a nested property on a WP API response object. Inspecting a collection of - * human-readable properties is usually a more comprehensible way to validate - * that the right results were returned than using IDs or arbitrary values. - * - * @example Pluck the rendered titles from the post - * - * const titles = getRenderedProp( collection, 'title' ); - * - * @example Create a bound variant that always plucks titles - * - * const getTitles = getRenderedProp.bind( null, 'title' ); - * const titles = getTitles( collection ); - * - * @private - * @param {String} property The name of the rendered property to pluck - * @param {Object[]} collection An array of response objects whence to pluck - * @returns {String[]} The collection of values for the rendered variants of - * the specified response object property - */ -module.exports = ( property, collection ) => collection - .map( item => item[ property ].rendered ); diff --git a/tests/helpers/http-test-utils.js b/tests/helpers/http-test-utils.js deleted file mode 100644 index 4e2eacfd..00000000 --- a/tests/helpers/http-test-utils.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; -const fs = require( 'fs' ); -const http = require( 'http' ); - -const expectStatusCode = ( url, code ) => new Promise( ( resolve, reject ) => { - const checkCode = ( actual, expected ) => { - if ( actual === expected ) { - return resolve( actual ); - } - reject( 'Expected ' + expected + ' but received ' + actual + ' for ' + url ); - }; - - http - .get( url, res => checkCode( res.statusCode, code ) ) - .on( 'error', ( error ) => { - if ( error.statusCode ) { - return checkCode( error.statusCode, error ); - } - return reject( error ); - } ); -} ); - -const expectFileEqualsURL = ( filePath, url ) => new Promise( ( resolve, reject ) => { - http.get( url, ( res ) => { - const data = []; - res.on( 'data', chunk => data.push( chunk ) ); // Append Buffer object - - res.on( 'error', error => reject( error ) ); - - res.on( 'end', () => { - const downloadedImageBuffer = Buffer.concat( data ); - const originalFile = fs.readFileSync( filePath ); - - const buffersEqual = downloadedImageBuffer.equals( originalFile ); - expect( buffersEqual ).toBe( true ); - - if ( buffersEqual ) { - return resolve( true ); - } - reject( new Error( 'Downloaded file did not match original' ) ); - } ); - } ); -} ); - -module.exports = { - expectStatusCode: expectStatusCode, - expectFileEqualsURL: expectFileEqualsURL, -}; diff --git a/tests/integration/assets/emilygarfield-untitled.jpg b/tests/integration/assets/emilygarfield-untitled.jpg deleted file mode 100644 index 8f4ec81b..00000000 Binary files a/tests/integration/assets/emilygarfield-untitled.jpg and /dev/null differ diff --git a/tests/integration/autodiscovery.js b/tests/integration/autodiscovery.js deleted file mode 100644 index 5af4a961..00000000 --- a/tests/integration/autodiscovery.js +++ /dev/null @@ -1,112 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Inspecting the titles of the returned posts arrays is an easy way to -// validate that the right page of results was returned -const getTitles = require( '../helpers/get-rendered-prop' ).bind( null, 'title' ); -const credentials = require( '../helpers/constants' ).credentials; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -// Define some arrays to use ensuring the returned data is what we expect -// it to be (e.g. an array of the titles from posts on the first page) -const expectedResults = { - firstPostTitle: 'Markup: HTML Tags and Formatting', -}; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: discover', ( transportName, WPAPI ) => { - let apiPromise; - - beforeAll( () => { - apiPromise = WPAPI.discover( 'http://wpapi.local' ); - // Stub warn and error - jest.spyOn( global.console, 'warn' ).mockImplementation( () => {} ); - jest.spyOn( global.console, 'error' ).mockImplementation( () => {} ); - } ); - - it( 'returns a promise', () => { - expect( apiPromise ).toBeInstanceOf( Promise ); - } ); - - it( 'eventually returns a configured WP instance', () => { - const prom = apiPromise - .then( ( result ) => { - expect( result ).toBeInstanceOf( WPAPI ); - expect( typeof result.namespace( 'wp/v2' ) ).toBe( 'object' ); - expect( typeof result.posts ).toBe( 'function' ); - expect( result.posts() ).toBeInstanceOf( WPRequest ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'auto-binds to the detected endpoint on the provided site', () => { - const prom = apiPromise - .then( ( site ) => { - expect( site.posts().toString() ).toBe( 'http://wpapi.local/wp-json/wp/v2/posts' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'resolves to correct endpoint if discovery targets /wp-json', () => { - const prom = WPAPI.discover( 'http://wpapi.local/wp-json' ) - .then( ( site ) => { - expect( site.posts().toString() ).toBe( 'http://wpapi.local/wp-json/wp/v2/posts' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'resolves to correct endpoint if discovery targets ?rest_route=/', () => { - const prom = WPAPI.discover( 'http://wpapi.local/?rest_route=/' ) - .then( ( site ) => { - expect( site.posts().toString() ).toBe( 'http://wpapi.local/wp-json/wp/v2/posts' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can correctly instantiate requests against the detected and bound site', () => { - const prom = apiPromise - .then( site => site.posts() ) - .then( ( posts ) => { - expect( getTitles( posts )[ 0 ] ).toBe( expectedResults.firstPostTitle ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'can authenticate', () => { - - it( 'requests against the detected and bound site', () => { - const prom = apiPromise - .then( site => site.auth( credentials ) ) - .then( site => site.users().me() ) - .then( ( user ) => { - expect( typeof user ).toBe( 'object' ); - expect( user.slug ).toBe( credentials.username ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'one-off requests against the detected and bound site', () => { - const prom = apiPromise - .then( site => site.users().auth( credentials ).me() ) - .then( ( user ) => { - expect( typeof user ).toBe( 'object' ); - expect( user.slug ).toBe( credentials.username ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - -} ); diff --git a/tests/integration/categories.js b/tests/integration/categories.js deleted file mode 100644 index 12a005fc..00000000 --- a/tests/integration/categories.js +++ /dev/null @@ -1,378 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Inspecting the names of the returned categories is an easy way to validate -// that the right page of results was returned -const getNames = require( '../helpers/get-prop' ).bind( null, 'name' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -// Define some arrays to use ensuring the returned data is what we expect -// it to be (e.g. an array of the names from categories on the first page) -const expectedResults = { - names: { - page1: [ - 'aciform', - 'antiquarianism', - 'arrangement', - 'asmodeus', - 'Blogroll', - 'broder', - 'buying', - 'Cat A', - 'Cat B', - 'Cat C', - ], - page2: [ - 'championship', - 'chastening', - 'Child 1', - 'Child 2', - 'Child Category 01', - 'Child Category 02', - 'Child Category 03', - 'Child Category 04', - 'Child Category 05', - 'clerkship', - ], - pageLast: [ - 'ween', - 'wellhead', - 'wellintentioned', - 'whetstone', - 'years', - ], - }, -}; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: categories()', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - it( 'can be used to retrieve a collection of category terms', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'retrieves the first 10 categories by default', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'paging properties', () => { - - it( 'are exposed as _paging on the response array', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( categories ).toHaveProperty( '_paging' ); - expect( typeof categories._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of categories', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( categories._paging ).toHaveProperty( 'total' ); - expect( categories._paging.total ).toBe( 65 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages available', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( categories._paging ).toHaveProperty( 'totalPages' ); - expect( categories._paging.totalPages ).toBe( 7 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the next page as .next', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( categories._paging ).toHaveProperty( 'next' ); - expect( typeof categories._paging.next ).toBe( 'object' ); - expect( categories._paging.next ).toBeInstanceOf( WPRequest ); - expect( categories._paging.next._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/categories?page=2' ); - // Get last page & ensure "next" no longer appears - return wp.categories() - .page( categories._paging.totalPages ) - .get() - .then( ( categories ) => { - expect( categories._paging ).not.toHaveProperty( 'next' ); - expect( getNames( categories ) ).toEqual( expectedResults.names.pageLast ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - return categories._paging.next - .get() - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 10 ); - expect( getNames( categories ) ).toEqual( expectedResults.names.page2 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the previous page as .prev', () => { - const prom = wp.categories() - .get() - .then( ( categories ) => { - expect( categories._paging ).not.toHaveProperty( 'prev' ); - return categories._paging.next - .get() - .then( ( categories ) => { - expect( categories._paging ).toHaveProperty( 'prev' ); - expect( typeof categories._paging.prev ).toBe( 'object' ); - expect( categories._paging.prev ).toBeInstanceOf( WPRequest ); - expect( categories._paging.prev._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/categories?page=1' ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the previous page of results via .prev', () => { - const prom = wp.categories() - .page( 2 ) - .get() - .then( ( categories ) => { - expect( getNames( categories ) ).toEqual( expectedResults.names.page2 ); - return categories._paging.prev - .get() - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 10 ); - expect( getNames( categories ) ).toEqual( expectedResults.names.page1 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'id()', () => { - - it( 'can be used to access an individual category term', () => { - let selectedCategory; - const prom = wp.categories() - .get() - .then( ( categories ) => { - // Pick one of the categories - selectedCategory = categories[ 3 ]; - // Query for that category directly - return wp.categories().id( selectedCategory.id ); - } ) - .then( ( category ) => { - expect( typeof category ).toBe( 'object' ); - expect( category ).toHaveProperty( 'id' ); - expect( category.id ).toBe( selectedCategory.id ); - expect( category ).toHaveProperty( 'slug' ); - expect( category.slug ).toBe( selectedCategory.slug ); - expect( category ).toHaveProperty( 'taxonomy' ); - expect( category.taxonomy ).toBe( 'category' ); - expect( category ).toHaveProperty( 'parent' ); - expect( category.parent ).toBe( 0 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'search()', () => { - - it( 'can be used to retrieve a category by slug', () => { - let selectedCategory; - const prom = wp.categories() - .get() - .then( ( categories ) => { - // Pick one of the categories - selectedCategory = categories[ 3 ]; - // Search for that category by slug - return wp.categories().search( selectedCategory.slug ); - } ) - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 1 ); - return categories[ 0 ]; - } ) - .then( ( category ) => { - expect( typeof category ).toBe( 'object' ); - expect( category ).toHaveProperty( 'id' ); - expect( category.id ).toBe( selectedCategory.id ); - expect( category ).toHaveProperty( 'slug' ); - expect( category.slug ).toBe( selectedCategory.slug ); - expect( category ).toHaveProperty( 'taxonomy' ); - expect( category.taxonomy ).toBe( 'category' ); - expect( category ).toHaveProperty( 'parent' ); - expect( category.parent ).toBe( 0 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'returns all categories matching the provided search string', () => { - const prom = wp.categories() - .search( 'parent' ) - .get() - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 4 ); - const slugs = categories.map( cat => cat.slug ).sort().join( ' ' ); - expect( slugs ).toBe( 'foo-a-foo-parent foo-parent parent parent-category' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be used to retrieve a category by slug from a set of search results', () => { - const prom = wp.categories() - .search( 'parent' ) - .get() - // Iterating over response of search is the best we can do until - // filtering for taxonomy term collections is reinstated - .then( categories => categories.find( cat => cat.slug === 'parent' ) ) - .then( ( category ) => { - expect( category ).toHaveProperty( 'slug' ); - expect( category.slug ).toBe( 'parent' ); - expect( category ).toHaveProperty( 'name' ); - expect( category.name ).toBe( 'Parent' ); - expect( category ).toHaveProperty( 'parent' ); - expect( category.parent ).toBe( 0 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'parent()', () => { - - it( 'can be used to retrieve direct children of a specific category', () => { - let parentCat; - let childCat1; - let childCat2; - // First, find the "parent" category - const prom = wp.categories() - .search( 'parent' ) - .get() - .then( ( categories ) => { - parentCat = categories.find( cat => cat.slug === 'parent' ); - return wp.categories().parent( parentCat.id ); - } ) - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 1 ); - const category = categories[ 0 ]; - expect( category ).toHaveProperty( 'name' ); - expect( category.name ).toBe( 'Child 1' ); - expect( category ).toHaveProperty( 'parent' ); - expect( category.parent ).toBe( parentCat.id ); - childCat1 = category; - // Go one level deeper - return wp.categories().parent( childCat1.id ); - } ) - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 1 ); - const category = categories[ 0 ]; - expect( category ).toHaveProperty( 'name' ); - expect( category.name ).toBe( 'Child 2' ); - expect( category ).toHaveProperty( 'parent' ); - expect( category.parent ).toBe( childCat1.id ); - childCat2 = category; - // Go one level deeper - return wp.categories().parent( childCat2.id ); - } ) - .then( ( categories ) => { - expect( Array.isArray( categories ) ).toBe( true ); - expect( categories.length ).toBe( 0 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( '.post()', () => { - - it( 'can be used to retrieve terms for a specific post', () => { - let postCategories; - const prom = wp.posts() - .perPage( 1 ) - .embed() - .get() - .then( ( posts ) => { - const post = posts[ 0 ]; - // Find the categories for this post - post._embedded['wp:term'].forEach( ( terms ) => { - if ( terms.length && terms[ 0 ].taxonomy === 'category' ) { - postCategories = terms; - } - } ); - const postId = post.id; - return wp.categories().post( postId ); - } ) - .then( ( categories ) => { - expect( categories.length ).toBe( postCategories.length ); - categories.forEach( ( cat, idx ) => { - [ - 'id', - 'name', - 'slug', - 'taxonomy', - ].forEach( ( prop ) => { - expect( cat[ prop ] ).toBe( postCategories[ idx ][ prop ] ); - } ); - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - -} ); diff --git a/tests/integration/comments.js b/tests/integration/comments.js deleted file mode 100644 index 9e7451b2..00000000 --- a/tests/integration/comments.js +++ /dev/null @@ -1,316 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Variable to use as our 'success token' in promise assertions -const SUCCESS = 'success'; - -// Define some arrays to use ensuring the returned data is what we expect -// it to be (e.g. an array of the titles from posts on the first page) -const expectedResults = { - postsAndAuthors: { - page1: [ - '1170John Doe', - '1148Jane Doe', - '1148John Doe', - '1148John Doe', - '1148Jane Doe', - '1148John Doe', - '1148Joe Bloggs', - '1148Jane Bloggs', - '1148Joe Bloggs', - ], - page2: [ - '1148Jane Bloggs', - '1148Joe Bloggs', - '1148Fred Bloggs', - '1148Fred Bloggs', - '1148Jane Bloggs', - '1148John Doe', - '1148John Doe', - '1148John Doe', - '1148Jane Doe', - '1148Anonymous User', - ], - page3: [ - '1148John Doe', - '1149John Doe', - '155John Doe', - '155Anon', - '155tellyworthtest2', - ], - }, - postsAndAuthorsAsc: { - page1: [ - '155tellyworthtest2', - '155Anon', - '155John Doe', - '1149John Doe', - '1148John Doe', - '1148Anonymous User', - '1148Jane Doe', - '1148John Doe', - '1148John Doe', - '1148John Doe', - ], - }, -}; - -// Inspecting the posts and authors of the returned comments arrays is an easy -// way to validate that the right page of results was returned -const getPostsAndAuthors = comments => comments - .map( comment => comment.post + comment.author_name ); - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: comments()', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - it( 'can be used to retrieve a list of comments, omitting a password-protected comment', () => { - const prom = wp.comments() - .get() - .then( ( comments ) => { - expect( Array.isArray( comments ) ).toBe( true ); - expect( comments.length ).toBe( 9 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'fetches the first page, omitting a password-protected comment', () => { - const prom = wp.comments() - .get() - .then( ( comments ) => { - expect( getPostsAndAuthors( comments ) ).toEqual( expectedResults.postsAndAuthors.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'fetches the 10 oldest comments when sorted "asc"', () => { - const prom = wp.comments() - .order( 'asc' ) - .get() - .then( ( comments ) => { - expect( getPostsAndAuthors( comments ) ).toEqual( expectedResults.postsAndAuthorsAsc.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'paging properties', () => { - - it( 'are exposed as _paging on the response array', () => { - const prom = wp.comments() - .get() - .then( ( posts ) => { - expect( posts ).toHaveProperty( '_paging' ); - expect( typeof posts._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of posts', () => { - const prom = wp.comments() - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'total' ); - expect( posts._paging.total ).toBe( 25 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages available', () => { - const prom = wp.comments() - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'totalPages' ); - expect( posts._paging.totalPages ).toBe( 3 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the next page as .next', () => { - const prom = wp.comments() - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'next' ); - expect( typeof posts._paging.next ).toBe( 'object' ); - expect( posts._paging.next ).toBeInstanceOf( WPRequest ); - expect( posts._paging.next._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/comments?page=2' ); - // Get last page & ensure 'next' no longer appears - return wp.comments() - .page( posts._paging.totalPages ) - .get() - .then( ( posts ) => { - expect( posts._paging ).not.toHaveProperty( 'next' ); - expect( getPostsAndAuthors( posts ) ).toEqual( expectedResults.postsAndAuthors.page3 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next', () => { - const prom = wp.comments() - .get() - .then( ( posts ) => { - return posts._paging.next - .get() - .then( ( posts ) => { - expect( Array.isArray( posts ) ).toBe( true ); - expect( posts.length ).toBe( 10 ); - expect( getPostsAndAuthors( posts ) ).toEqual( expectedResults.postsAndAuthors.page2 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the previous page as .prev', () => { - const prom = wp.comments() - .get() - .then( ( posts ) => { - expect( posts._paging ).not.toHaveProperty( 'prev' ); - return posts._paging.next - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'prev' ); - expect( typeof posts._paging.prev ).toBe( 'object' ); - expect( posts._paging.prev ).toBeInstanceOf( WPRequest ); - expect( posts._paging.prev._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/comments?page=1' ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the previous page of results via .prev', () => { - const prom = wp.comments() - .page( 2 ) - .get() - .then( ( posts ) => { - expect( getPostsAndAuthors( posts ) ).toEqual( expectedResults.postsAndAuthors.page2 ); - return posts._paging.prev - .get() - .then( ( posts ) => { - expect( Array.isArray( posts ) ).toBe( true ); - // 9 because one comment is for a password-protected post - expect( posts.length ).toBe( 9 ); - expect( getPostsAndAuthors( posts ) ).toEqual( expectedResults.postsAndAuthors.page1 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'querying by ID', () => { - let commentCollection; - let commentId; - let commentProm; - - beforeEach( () => { - commentCollection = []; - commentProm = wp.comments() - .get() - .then( ( comments ) => { - commentCollection = comments; - commentId = commentCollection[4].id; - return wp.comments() - .id( commentId ) - .get(); - } ); - } ); - - it( 'returns an object, not an array', () => { - const prom = commentProm - .then( ( comment ) => { - expect( Array.isArray( comment ) ).toBe( false ); - expect( typeof comment ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'returns the correct comment', () => { - const prom = commentProm.then( ( comment ) => { - expect( comment.id ).toBe( commentId ); - [ 'author_name', 'post', 'parent', 'date', 'status' ].forEach( ( prop ) => { - expect( comment[ prop ] ).toBe( commentCollection[4][ prop ] ); - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( '.post() query', () => { - let pageComments; - let commentProm; - - beforeEach( () => { - const pageId = 155; - commentProm = wp.pages() - .id( pageId ) - .embed() - .get() - .then( ( page ) => { - // Do a flatten reduction because .replies will be an array of arrays - pageComments = page._embedded.replies.reduce( ( flatArr, arr ) => flatArr.concat( arr ), [] ); - return wp.comments() - .post( pageId ) - .get(); - } ); - } ); - - it( 'returns an array of posts', () => { - const prom = commentProm - .then( ( comments ) => { - expect( Array.isArray( comments ) ).toBe( true ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'returns the correct number of comments', () => { - const prom = commentProm - .then( ( comments ) => { - expect( comments.length ).toBe( 3 ); - expect( comments.length ).toBe( pageComments.length ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'returns the correct comments', () => { - const prom = commentProm - .then( ( comments ) => { - pageComments.forEach( ( comment, i ) => { - [ 'id', 'parent', 'author', 'author_name' ].forEach( ( prop ) => { - expect( comment[ prop ] ).toBe( comments[ i ][ prop ] ); - } ); - expect( comment.content.rendered ).toBe( comments[ i ].content.rendered ); - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - -} ); diff --git a/tests/integration/custom-http-headers.js b/tests/integration/custom-http-headers.js deleted file mode 100644 index 8ed132ea..00000000 --- a/tests/integration/custom-http-headers.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -// Inspecting the titles of the returned posts arrays is an easy way to -// validate that the right page of results was returned -const getTitles = require( '../helpers/get-rendered-prop' ).bind( null, 'title' ); -const credentials = require( '../helpers/constants' ).credentials; -const base64credentials = Buffer.from( `${ credentials.username }:${ credentials.password }` ).toString( 'base64' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: custom HTTP Headers', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - // Testing basic authentication is an acceptable proxy for whether a header - // value (Authentication:, in this case) is being set - it( 'can be provided using WPRequest#setHeaders()', () => { - const prom = wp.posts() - .setHeaders( 'Authorization', 'Basic ' + base64credentials ) - .status( [ 'future', 'draft' ] ) - .get() - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( [ - 'Scheduled', - 'Draft', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be provided at the WPAPI instance level using WPAPI#setHeaders()', () => { - const authenticated = WPAPI - .site( 'http://wpapi.local/wp-json' ) - .setHeaders( 'Authorization', 'Basic ' + base64credentials ); - const prom = authenticated.posts() - .status( [ 'future', 'draft' ] ) - .get() - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( [ - 'Scheduled', - 'Draft', - ] ); - return authenticated.users().me(); - } ) - .then( ( me ) => { - expect( me.slug ).toBe( 'admin' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/integration/custom-http-transport.js b/tests/integration/custom-http-transport.js deleted file mode 100644 index 876c248f..00000000 --- a/tests/integration/custom-http-transport.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; - -const credentials = require( '../helpers/constants' ).credentials; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ), require( '../../superagent/superagent-transport' ) ], - [ 'wpapi/fetch', require( '../../fetch' ), require( '../../fetch/fetch-transport' ) ], -] )( '%s: custom HTTP transport methods', ( transportName, WPAPI, httpTransport ) => { - let wp; - let id; - let cache; - let cachingGet; - - beforeEach( () => { - cache = {}; - cachingGet = jest.fn( ( wpreq ) => { - const result = cache[ wpreq ]; - // If a cache hit is found, return it via the same promise - // signature as that of the default transport method - if ( result ) { - return Promise.resolve( result ); - } - - // Delegate to default transport if no cached data was found - return WPAPI.transport.get( wpreq ).then( ( result ) => { - cache[ wpreq ] = result; - return result; - } ); - } ); - - return WPAPI.site( 'http://wpapi.local/wp-json' ) - .posts() - .perPage( 1 ) - .then( ( posts ) => { - id = posts[ 0 ].id; - - // Set up our spy here so the request to get the ID isn't counted - jest.spyOn( httpTransport, 'get' ); - } ); - } ); - - afterEach( () => { - httpTransport.get.mockRestore(); - } ); - - it( 'can be defined to e.g. use a cache when available', () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - transport: { - get: cachingGet, - }, - } ).auth( credentials ); - - const query1 = wp.posts().id( id ); - let query2; - - const prom = query1 - .get() - .then( ( result ) => { - expect( result.id ).toBe( id ); - expect( cachingGet ).toBeCalledTimes( 1 ); - expect( httpTransport.get ).toHaveBeenCalledTimes( 1 ); - expect( httpTransport.get ).toHaveBeenCalledWith( query1 ); - expect( result ).toBe( cache[ 'http://wpapi.local/wp-json/wp/v2/posts/' + id ] ); - } ) - .then( () => { - query2 = wp.posts().id( id ); - return query2.get(); - } ) - .then( ( result ) => { - expect( cachingGet ).toBeCalledTimes( 2 ); - expect( httpTransport.get ).toHaveBeenCalledTimes( 1 ); - expect( httpTransport.get ).toHaveBeenLastCalledWith( query1 ); - expect( httpTransport.get.mock.calls[0][0] ).not.toBe( query2 ); - expect( result ).toBe( cache[ 'http://wpapi.local/wp-json/wp/v2/posts/' + id ] ); - return SUCCESS; - } ); - - return prom; - - // return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be defined to transform responses', () => { - const extractSlug = ( results ) => { - if ( Array.isArray( results ) && results.length === 1 ) { - return results[0]; - } - return results; - }; - - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - transport: { - // If .slug is used, auto-unwrap the returned array - get( wpreq ) { - if ( ! wpreq._params.slug ) { - return WPAPI.transport.get.call( this, wpreq ); - } - return WPAPI.transport.get( wpreq ).then( ( results ) => { - return extractSlug( results ); - } ); - }, - }, - } ); - - const prom = wp.posts().slug( 'template-more-tag' ) - .then( ( results ) => { - expect( typeof results ).toBe( 'object' ); - expect( Array.isArray( results ) ).toBe( false ); - expect( results.title.rendered ).toBe( 'Template: More Tag' ); - return SUCCESS; - } ); - - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be defined to augment responses', () => { - class Collection { - constructor( arr ) { - this.data = arr; - } - pluck( key ) { - return this.data.map( val => val[ key ] ); - } - } - - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - transport: { - // Add collection helper methods to the returned arrays - get( wpreq, cb ) { - return WPAPI.transport.get.call( this, wpreq, cb ).then( ( results ) => { - if ( Array.isArray( results ) ) { - return new Collection( results ); - } - } ); - }, - }, - } ); - - const prom = wp.posts() - .then( ( results ) => { - expect( results ).toBeInstanceOf( Collection ); - expect( results.pluck( 'slug' ) ).toEqual( [ - 'markup-html-tags-and-formatting', - 'markup-image-alignment', - 'markup-text-alignment', - 'title-with-special-characters', - 'markup-title-with-markup', - 'template-featured-image-vertical', - 'template-featured-image-horizontal', - 'template-more-tag', - 'template-excerpt-defined', - 'template-excerpt-generated', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/integration/error-states.js b/tests/integration/error-states.js deleted file mode 100644 index 31b7abf7..00000000 --- a/tests/integration/error-states.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: error states:', ( transportName, WPAPI ) => { - - it( 'invalid root endpoint causes a transport-level 404 error', () => { - const wp = WPAPI.site( 'http://wpapi.local/wrong-root-endpoint' ); - const prom = wp.posts() - .get() - .catch( ( err ) => { - // expect( err ).toBeInstanceOf( Error ); - expect( err ).toHaveProperty( 'status' ); - expect( err.status ).toBe( 404 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/integration/media.js b/tests/integration/media.js deleted file mode 100644 index c0432113..00000000 --- a/tests/integration/media.js +++ /dev/null @@ -1,365 +0,0 @@ -'use strict'; - -const path = require( 'path' ); -const objectReduce = require( '../../lib/util/object-reduce' ); -const httpTestUtils = require( '../helpers/http-test-utils' ); -const unique = require( '../../lib/util/unique' ); - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Inspecting the titles of the returned posts arrays is an easy way to -// validate that the right page of results was returned -const getTitles = require( '../helpers/get-rendered-prop' ).bind( null, 'title' ); -const credentials = require( '../helpers/constants' ).credentials; - -const filePath = path.join( __dirname, 'assets/emilygarfield-untitled.jpg' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -const expectedResults = { - titles: { - page1: [ - 'spectacles', - 'dsc20050315_145007_132', - 'dsc20050604_133440_34211', - 'dsc20040724_152504_532', - 'triforce-wallpaper', - 'Vertical Featured Image', - 'Horizontal Featured Image', - 'Unicorn Wallpaper', - 'Image Alignment 1200×4002', - 'Image Alignment 580×300', - ], - page2: [ - 'Image Alignment 300×200', - 'Image Alignment 150×150', - 'I Am Worth Loving Wallpaper', - 'OLYMPUS DIGITAL CAMERA', - 'St. Louis Blues', - 'dsc20050604_133440_3421', - 'dsc20040724_152504_532', - 'Boat Barco Texture', - 'Huatulco Coastline', - 'Brazil Beach', - ], - page4: [ - 'Yachtsody in Blue', - 'Boardwalk', - 'Sunburst Over River', - 'Golden Gate Bridge', - 'Bell on Wharf', - 'dsc20050813_115856_52', - 'dsc20050727_091048_222', - 'canola2', - ], - }, -}; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: media()', ( transportName, WPAPI ) => { - let wp; - let authenticated; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - authenticated = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ).auth( credentials ); - } ); - - it( 'an be used to retrieve a list of media items', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - expect( Array.isArray( media ) ).toBe( true ); - expect( media.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'fetches the 10 most recent media by default', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - expect( getTitles( media ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'paging properties', () => { - - it( 'are exposed as _paging on the response array', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - expect( media ).toHaveProperty( '_paging' ); - expect( typeof media._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of media: use .headers() for coverage reasons', () => { - const prom = wp.media() - .headers() - .then( ( postHeadersResponse ) => { - expect( postHeadersResponse ).toHaveProperty( 'x-wp-total' ); - expect( postHeadersResponse[ 'x-wp-total' ] ).toBe( '38' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages available', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - expect( media._paging ).toHaveProperty( 'totalPages' ); - expect( media._paging.totalPages ).toBe( 4 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the next page as .next', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - expect( media._paging ).toHaveProperty( 'next' ); - expect( typeof media._paging.next ).toBe( 'object' ); - expect( media._paging.next ).toBeInstanceOf( WPRequest ); - expect( media._paging.next._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/media?page=2' ); - // Get last page & ensure "next" no longer appears - return wp.media() - .page( media._paging.totalPages ) - .get() - .then( ( media ) => { - expect( media._paging ).not.toHaveProperty( 'next' ); - expect( getTitles( media ) ).toEqual( expectedResults.titles.page4 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - return media._paging.next - .get() - .then( ( media ) => { - expect( Array.isArray( media ) ).toBe( true ); - expect( media.length ).toBe( 10 ); - expect( getTitles( media ) ).toEqual( expectedResults.titles.page2 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the previous page as .prev', () => { - const prom = wp.media() - .get() - .then( ( media ) => { - expect( media._paging ).not.toHaveProperty( 'prev' ); - return media._paging.next - .get() - .then( ( media ) => { - expect( media._paging ).toHaveProperty( 'prev' ); - expect( typeof media._paging.prev ).toBe( 'object' ); - expect( media._paging.prev ).toBeInstanceOf( WPRequest ); - expect( media._paging.prev._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/media?page=1' ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the previous page of results via .prev', () => { - const prom = wp.media() - .page( 2 ) - .get() - .then( ( media ) => { - expect( getTitles( media ) ).toEqual( expectedResults.titles.page2 ); - return media._paging.prev - .get() - .then( ( media ) => { - expect( Array.isArray( media ) ).toBe( true ); - expect( media.length ).toBe( 10 ); - expect( getTitles( media ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'without authentication', () => { - - it( 'cannot POST', () => { - const prom = wp.media() - .file( filePath ) - .create( { - title: 'Media File', - content: 'Some Content', - } ) - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_cannot_create' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'cannot PUT', () => { - const prom = wp.media() - .perPage( 1 ) - .get() - .then( ( media ) => { - const id = media[ 0 ].id; - return wp.media() - .id( id ) - .update( { - title: 'New Title', - } ); - } ) - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_cannot_edit' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'cannot DELETE', () => { - const prom = wp.media() - .perPage( 1 ) - .get() - .then( ( media ) => { - const id = media[ 0 ].id; - return wp.media().id( id ).delete( { - force: true, - } ); - } ) - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_cannot_delete' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - it( 'can create, update & delete media when authenticated', () => { - let id; - let imageUrl; - - // CREATE - const prom = authenticated.media() - .file( filePath, 'ehg-conduits.jpg' ) - .create( { - title: 'Untitled', - caption: 'A painting from Emily Garfield\'s "Conduits" series', - } ) - .then( ( createdMedia ) => { - id = createdMedia.id; - imageUrl = createdMedia.source_url; - expect( createdMedia.title.rendered ).toBe( 'Untitled' ); - expect( createdMedia.caption.raw ).toBe( 'A painting from Emily Garfield\'s "Conduits" series' ); - - // File name is correctly applied and image was uploaded to content dir - expect( imageUrl ).toMatch( /^http:\/\/wpapi.local\/content\/uploads\/.*\/ehg-conduits.jpg$/ ); - } ) - // UPDATE - .then( () => authenticated.media() - .id( id ) - .update( { - title: 'Conduits Series', - alt_text: 'A photograph of an abstract painting by Emily Garfield', - } ) - ) - .then( ( result ) => { - expect( result.id ).toBe( id ); - expect( result.title.rendered ).toBe( 'Conduits Series' ); - expect( result.alt_text ).toBe( 'A photograph of an abstract painting by Emily Garfield' ); - return result; - } ) - // READ - // Validate thumbnails were created - .then( ( result ) => { - const sizes = result.media_details.sizes; - const sizeURLs = objectReduce( sizes, ( urls, size ) => urls.concat( size.source_url ), [] ); - - // Expect all sizes to have different URLs - expect( Object.keys( sizes ).length ).toBe( unique( sizeURLs ).length ); - return sizeURLs.reduce( ( previous, sizeURL ) => previous.then( () => ( - httpTestUtils.expectStatusCode( sizeURL, 200 ) - ) ), Promise.resolve() ); - } ) - // Validate image was uploaded correctly - .then( () => { - return httpTestUtils.expectFileEqualsURL( filePath, imageUrl ); - } ) - // DELETE - .then( () => { - // Attempt to delete media: expect this to fail, since media does not - // support being trashed and can only be permanently removed - return authenticated.media() - .id( id ) - .delete(); - } ) - .catch( ( error ) => { - expect( error.code ).toBe( 'rest_trash_not_supported' ); - expect( error.data ).toEqual( { - status: 501, - } ); - // Now permanently delete this media - return authenticated.media() - .id( id ) - .delete( { - force: true, - } ); - } ) - .then( ( response ) => { - expect( typeof response ).toBe( 'object' ); - // DELETE action returns the media object as the .previous property - expect( typeof response.previous ).toBe( 'object' ); - expect( response.previous.id ).toBe( id ); - // Query for the media: expect this to fail, since it has been deleted - return wp.media().id( id ); - } ) - .catch( ( error ) => { - expect( error.code ).toBe( 'rest_post_invalid_id' ); - expect( error.data ).toEqual( { - status: 404, - } ); - } ) - // Validate image file has been removed - .then( () => { - return httpTestUtils.expectStatusCode( imageUrl, 404 ); - } ) - .then( () => { - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/integration/pages.js b/tests/integration/pages.js deleted file mode 100644 index 9554dc73..00000000 --- a/tests/integration/pages.js +++ /dev/null @@ -1,203 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Inspecting the titles of the returned posts arrays is an easy way to -// validate that the right page of results was returned -const getTitles = require( '../helpers/get-rendered-prop' ).bind( null, 'title' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -// Define some arrays to use ensuring the returned data is what we expect -// it to be (e.g. an array of the titles from pages on the first page) -const expectedResults = { - titles: { - page1: [ - 'Page Markup And Formatting', - 'Page Image Alignment', - 'Level 3b', - 'Level 3a', - 'Level 2b', - 'Level 2a', - 'Page B', - 'Page A', - 'Blog', - 'Front Page', - ], - page2: [ - 'Clearing Floats', - 'About The Tests', - 'Level 1', - 'Level 2', - 'Level 3', - 'Page with comments disabled', - 'Page with comments', - 'Lorem Ipsum', - ], - }, -}; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: pages()', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - it( 'can be used to retrieve a list of recent pages', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( Array.isArray( pages ) ).toBe( true ); - expect( pages.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'fetches the 10 most recent pages by default', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( getTitles( pages ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'paging properties', () => { - - it( 'are exposed as _paging on the response array', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( pages ).toHaveProperty( '_paging' ); - expect( typeof pages._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( pages._paging ).toHaveProperty( 'total' ); - expect( pages._paging.total ).toBe( 18 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages available', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( pages._paging ).toHaveProperty( 'totalPages' ); - expect( pages._paging.totalPages ).toBe( 2 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the next page as .next', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( pages._paging ).toHaveProperty( 'next' ); - expect( typeof pages._paging.next ).toBe( 'object' ); - expect( pages._paging.next ).toBeInstanceOf( WPRequest ); - expect( pages._paging.next._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/pages?page=2' ); - // Get last page & ensure "next" no longer appears - return wp.pages() - .page( pages._paging.totalPages ) - .get() - .then( ( pages ) => { - expect( pages._paging ).not.toHaveProperty( 'next' ); - expect( getTitles( pages ) ).toEqual( expectedResults.titles.page2 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next', () => { - const prom = wp.pages() - .get() - .then( pages => pages._paging.next.get() ) - .then( ( pages ) => { - expect( Array.isArray( pages ) ).toBe( true ); - expect( pages.length ).toBe( 8 ); - expect( getTitles( pages ) ).toEqual( expectedResults.titles.page2 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the previous page as .prev', () => { - const prom = wp.pages() - .get() - .then( ( pages ) => { - expect( pages._paging ).not.toHaveProperty( 'prev' ); - return pages._paging.next.get(); - } ) - .then( ( pages ) => { - expect( pages._paging ).toHaveProperty( 'prev' ); - expect( typeof pages._paging.prev ).toBe( 'object' ); - expect( pages._paging.prev ).toBeInstanceOf( WPRequest ); - expect( pages._paging.prev._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/pages?page=1' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the previous page of results via .prev', () => { - const prom = wp.pages() - .page( 2 ) - .get() - .then( ( pages ) => { - expect( getTitles( pages ) ).toEqual( expectedResults.titles.page2 ); - return pages._paging.prev.get(); - } ) - .then( ( pages ) => { - expect( Array.isArray( pages ) ).toBe( true ); - expect( pages.length ).toBe( 10 ); - expect( getTitles( pages ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'filter methods', () => { - - describe( 'slug', () => { - - it( 'can be used to return only pages with the specified slug', () => { - const prom = wp.pages() - .slug( 'clearing-floats' ) - .get() - .then( ( pages ) => { - expect( pages.length ).toBe( 1 ); - expect( getTitles( pages ) ).toEqual( [ - 'Clearing Floats', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - } ); - -} ); diff --git a/tests/integration/posts.js b/tests/integration/posts.js deleted file mode 100644 index 42a91784..00000000 --- a/tests/integration/posts.js +++ /dev/null @@ -1,799 +0,0 @@ -'use strict'; - -const path = require( 'path' ); - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Inspecting the titles of the returned posts arrays is an easy way to -// validate that the right page of results was returned -const getTitles = require( '../helpers/get-rendered-prop' ).bind( null, 'title' ); -const credentials = require( '../helpers/constants' ).credentials; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -// Define some arrays to use ensuring the returned data is what we expect -// it to be (e.g. an array of the titles from posts on the first page) -const expectedResults = { - titles: { - page1: [ - 'Markup: HTML Tags and Formatting', - 'Markup: Image Alignment', - 'Markup: Text Alignment', - 'Markup: Title With Special Characters', - 'Markup: Title With Markup', - 'Template: Featured Image (Vertical)', - 'Template: Featured Image (Horizontal)', - 'Template: More Tag', - 'Template: Excerpt (Defined)', - 'Template: Excerpt (Generated)', - ], - page2: [ - 'Template: Paginated', - 'Template: Sticky', - 'Template: Password Protected (the password is “enter”)', - 'Template: Comments', - 'Template: Comments Disabled', - 'Template: Pingbacks And Trackbacks', - 'Media: Twitter Embeds', - 'Post Format: Standard', - 'Post Format: Gallery', - 'Post Format: Gallery (Tiled)', - ], - page4: [ - 'Post Format: Quote', - 'Post Format: Chat', - 'Antidisestablishmentarianism', - '', - 'Edge Case: No Content', - 'Edge Case: Many Categories', - 'Edge Case: Many Tags', - 'Edge Case: Nested And Mixed Lists', - ], - }, -}; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: posts()', ( transportName, WPAPI ) => { - let wp; - let authenticated; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - authenticated = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ).auth( credentials ); - } ); - - it( 'can be used to retrieve a list of recent posts', () => { - const prom = wp.posts() - .get() - .then( ( posts ) => { - expect( Array.isArray( posts ) ).toBe( true ); - expect( posts.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'fetches the 10 most recent posts by default', () => { - const prom = wp.posts() - .get() - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'properly parses responses returned from server as text/html', () => { - const prom = wp.posts() - .param( '_wpapi_force_html', true ) - .get() - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'paging properties', () => { - - it( 'are exposed as _paging on the response array', () => { - const prom = wp.posts() - .get() - .then( ( posts ) => { - expect( posts ).toHaveProperty( '_paging' ); - expect( typeof posts._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'are exposed as _paging on the response array when response is text/html', () => { - const prom = wp.posts() - .param( '_wpapi_force_html', true ) - .get() - .then( ( posts ) => { - expect( posts ).toHaveProperty( '_paging' ); - expect( typeof posts._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of posts: use .headers() for coverage reasons', () => { - const prom = wp.posts() - .headers() - .then( ( postHeadersResponse ) => { - expect( postHeadersResponse ).toHaveProperty( 'x-wp-total' ); - expect( postHeadersResponse[ 'x-wp-total' ] ).toBe( '38' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages available', () => { - const prom = wp.posts() - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'totalPages' ); - expect( posts._paging.totalPages ).toBe( 4 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the next page as .next', () => { - const prom = wp.posts() - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'next' ); - expect( typeof posts._paging.next ).toBe( 'object' ); - expect( posts._paging.next ).toBeInstanceOf( WPRequest ); - expect( posts._paging.next._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/posts?page=2' ); - // Get last page & ensure "next" no longer appears - return wp.posts() - .page( posts._paging.totalPages ) - .get() - .then( ( posts ) => { - expect( posts._paging ).not.toHaveProperty( 'next' ); - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page4 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next', () => { - const prom = wp.posts() - .get() - .then( posts => posts._paging.next.get() ) - - .then( ( posts ) => { - expect( Array.isArray( posts ) ).toBe( true ); - expect( posts.length ).toBe( 10 ); - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page2 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next when response is text/html', () => { - const prom = wp.posts() - .param( '_wpapi_force_html', true ) - .get() - .then( posts => posts._paging.next.get() ) - .then( ( posts ) => { - expect( Array.isArray( posts ) ).toBe( true ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the previous page as .prev', () => { - const prom = wp.posts() - .get() - .then( ( posts ) => { - expect( posts._paging ).not.toHaveProperty( 'prev' ); - return posts._paging.next - .get() - .then( ( posts ) => { - expect( posts._paging ).toHaveProperty( 'prev' ); - expect( typeof posts._paging.prev ).toBe( 'object' ); - expect( posts._paging.prev ).toBeInstanceOf( WPRequest ); - expect( posts._paging.prev._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/posts?page=1' ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the previous page of results via .prev', () => { - const prom = wp.posts() - .page( 2 ) - .get() - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page2 ); - return posts._paging.prev - .get() - .then( ( posts ) => { - expect( Array.isArray( posts ) ).toBe( true ); - expect( posts.length ).toBe( 10 ); - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page1 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'maintains authentication across paging requests', () => { - const prom = authenticated.posts() - .context( 'edit' ) - .get() - .then( posts => posts._paging.next.get() ) - .then( ( page2 ) => { - expect( page2[0].content ).toHaveProperty( 'raw' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'filter methods', () => { - - describe( 'slug', () => { - - it( 'can be used to return only posts with the specified slug', () => { - const prom = wp.posts() - .slug( 'template-excerpt-generated' ) - .get() - .then( ( posts ) => { - expect( posts.length ).toBe( 1 ); - expect( getTitles( posts ) ).toEqual( [ - 'Template: Excerpt (Generated)', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'status', () => { - - it( 'can be used to retrieve specific statuses of posts', () => { - const prom = authenticated.posts() - .status( [ 'future', 'draft' ] ) - .get() - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( [ - 'Scheduled', - 'Draft', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'tags', () => { - - it( 'can be used to return only posts with a provided tag', () => { - const prom = wp.tags() - .slug( 'title' ) - .get() - .then( ( tags ) => { - const tagIDs = tags.map( tag => tag.id ); - return wp.posts().tags( tagIDs ); - } ) - .then( ( posts ) => { - expect( posts.length ).toBe( 5 ); - expect( getTitles( posts ) ).toEqual( [ - 'Markup: Title With Special Characters', - 'Markup: Title With Markup', - 'Antidisestablishmentarianism', - '', - 'Edge Case: Many Tags', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be used to return posts with any of the provided tags', () => { - const prom = Promise - .all( [ - wp.tags().search( 'featured image' ), - wp.tags().search( 'embeds' ), - ] ) - .then( ( results ) => { - const tagIDs = results.reduce( ( ids, arr ) => ( - ids.concat( arr.map( tag => tag.id ) ) - ), [] ); - return wp.posts().tags( tagIDs ); - } ) - .then( ( posts ) => { - expect( posts.length ).toBe( 6 ); - expect( getTitles( posts ) ).toEqual( [ - 'Template: Featured Image (Vertical)', - 'Template: Featured Image (Horizontal)', - 'Media: Twitter Embeds', - 'Post Format: Video (WordPress.tv)', - 'Post Format: Video (VideoPress)', - 'Edge Case: Many Tags', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'excludeTags', () => { - - it( 'can be used to omit posts in specific tags', () => { - const prom = Promise - .all( [ - wp.tags().search( 'css' ), - wp.tags().search( 'content' ), - ] ) - .then( ( results ) => { - const tagIDs = results.reduce( ( ids, arr ) => ( - ids.concat( arr.map( tag => tag.id ) ) - ), [] ); - return wp.posts().excludeTags( tagIDs ); - } ) - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( [ - 'Markup: Title With Special Characters', - 'Template: Featured Image (Vertical)', - 'Template: Featured Image (Horizontal)', - 'Template: Sticky', - 'Template: Password Protected (the password is “enter”)', - 'Template: Comments', - 'Template: Comments Disabled', - 'Template: Pingbacks And Trackbacks', - 'Post Format: Standard', - 'Post Format: Gallery', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'categories', () => { - - it( 'can be used to return only posts with a provided category', () => { - const prom = wp.categories() - .slug( 'markup' ) - .get() - .then( ( categories ) => { - const categoryIDs = categories.map( cat => cat.id ); - return wp.posts().categories( categoryIDs ); - } ) - .then( ( posts ) => { - expect( posts.length ).toBe( 6 ); - expect( getTitles( posts ) ).toEqual( [ - 'Markup: HTML Tags and Formatting', - 'Markup: Image Alignment', - 'Markup: Text Alignment', - 'Markup: Title With Special Characters', - 'Markup: Title With Markup', - 'Edge Case: Many Categories', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be used to return posts with any of the provided categories', () => { - const prom = Promise - .all( [ - wp.categories().search( 'edge case' ), - wp.categories().search( 'pustule' ), - ] ) - .then( ( results ) => { - const categoriesIDs = results.reduce( ( ids, arr ) => ( - ids.concat( arr.map( cat => cat.id ) ) - ), [] ); - return wp.posts().categories( categoriesIDs ); - } ) - .then( ( posts ) => { - expect( posts.length ).toBe( 6 ); - expect( getTitles( posts ) ).toEqual( [ - 'Antidisestablishmentarianism', - '', - 'Edge Case: No Content', - 'Edge Case: Many Categories', - 'Edge Case: Many Tags', - 'Edge Case: Nested And Mixed Lists', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'excludeCategories', () => { - - it( 'can be used to omit posts in specific categories', () => { - const prom = Promise - .all( [ - wp.categories().slug( 'markup' ), - wp.categories().slug( 'post-formats' ), - ] ) - .then( ( results ) => { - const tagIDs = results.reduce( ( ids, arr ) => ( - ids.concat( arr.map( tag => tag.id ) ) - ), [] ); - return wp.posts().excludeCategories( tagIDs ); - } ) - .then( ( posts ) => { - expect( getTitles( posts ) ).toEqual( [ - 'Template: Featured Image (Vertical)', - 'Template: Featured Image (Horizontal)', - 'Template: More Tag', - 'Template: Excerpt (Defined)', - 'Template: Excerpt (Generated)', - 'Template: Paginated', - 'Template: Sticky', - 'Template: Password Protected (the password is “enter”)', - 'Template: Comments', - 'Template: Comments Disabled', - ] ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'before', () => { - - it( 'can be used to return only posts from before a certain date', () => { - const prom = wp.posts() - .before( '2013-01-08' ) - .then( ( posts ) => { - expect( posts[0].title.rendered ).toBe( 'Markup: Title With Special Characters' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'after', () => { - - it( 'can be used to return only posts from after a certain date', () => { - const prom = wp.posts() - .after( '2013-01-08' ) - .then( ( posts ) => { - expect( posts.length ).toBe( 3 ); - expect( getTitles( posts ) ).toEqual( expectedResults.titles.page1.slice( 0, 3 ) ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - } ); - - // Post creation, update & deletion suites - describe( 'authorization errors', () => { - - it( 'cannot use context=edit without authentication', () => { - const prom = wp.posts() - .edit() - .get() - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_forbidden_context' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'cannot DELETE without authentication', () => { - let id; - const prom = wp.posts() - .perPage( 1 ) - .get() - .then( ( posts ) => { - id = posts[ 0 ].id; - return wp.posts() - .id( id ) - .delete(); - } ) - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_cannot_delete' ); - expect( err.data ).toEqual( { - status: 401, - } ); - // Ensure that the post was NOT deleted by querying for it again - return wp.posts() - .id( id ) - .get(); - } ) - .then( ( result ) => { - expect( result ).toHaveProperty( 'id' ); - expect( result.id ).toBe( id ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'cannot create (POST) without authentication', () => { - const prom = wp.posts() - .create( { - title: 'New Post 2501', - content: 'Some Content', - } ) - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_cannot_create' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'cannot update (PUT) without authentication', () => { - let id; - const prom = wp.posts() - .perPage( 1 ) - .get() - .then( ( posts ) => { - id = posts[ 0 ].id; - return wp.posts() - .id( id ) - .update( { - title: 'New Post 2501', - content: 'Some Content', - } ); - } ) - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_cannot_edit' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - } ); - - it( 'can create, update & delete a post when authenticated', () => { - let id; - const prom = authenticated.posts() - .create( { - title: 'New Post 2501', - content: 'Some Content', - } ) - .then( ( createdPost ) => { - id = createdPost.id; - expect( typeof createdPost ).toBe( 'object' ); - expect( createdPost ).toHaveProperty( 'status' ); - expect( createdPost.status ).toBe( 'draft' ); - expect( createdPost ).toHaveProperty( 'title' ); - expect( createdPost.title ).toHaveProperty( 'raw' ); - expect( createdPost.title.raw ).toBe( 'New Post 2501' ); - expect( createdPost ).toHaveProperty( 'content' ); - expect( createdPost.content ).toHaveProperty( 'raw' ); - expect( createdPost.content.raw ).toBe( 'Some Content' ); - return authenticated.posts() - .id( id ) - .update( { - title: 'Updated Title', - status: 'publish', - } ); - } ) - .then( ( updatedPost ) => { - expect( typeof updatedPost ).toBe( 'object' ); - expect( updatedPost ).toHaveProperty( 'id' ); - expect( updatedPost.id ).toBe( id ); - expect( updatedPost ).toHaveProperty( 'status' ); - expect( updatedPost.status ).toBe( 'publish' ); - expect( updatedPost ).toHaveProperty( 'title' ); - expect( updatedPost.title ).toHaveProperty( 'raw' ); - expect( updatedPost.title.raw ).toBe( 'Updated Title' ); - expect( updatedPost ).toHaveProperty( 'content' ); - expect( updatedPost.content ).toHaveProperty( 'raw' ); - expect( updatedPost.content.raw ).toBe( 'Some Content' ); - // Ensure that, now that it is published, we can query for this post - // without authentication - return wp.posts().id( id ); - } ) - .then( ( post ) => { - expect( typeof post ).toBe( 'object' ); - expect( post ).toHaveProperty( 'id' ); - expect( post.id ).toBe( id ); - expect( post ).toHaveProperty( 'title' ); - expect( post.title ).toHaveProperty( 'rendered' ); - expect( post.title.rendered ).toBe( 'Updated Title' ); - // Re-authenticate & delete (trash) this post - return authenticated.posts().id( id ).delete(); - } ) - .then( ( response ) => { - expect( typeof response ).toBe( 'object' ); - // DELETE action returns the post object - expect( response.id ).toBe( id ); - // Query for the post: expect this to fail, since it is trashed and - // the unauthenticated user does not have permissions to see it - return wp.posts().id( id ); - } ) - .catch( ( error ) => { - expect( error.code ).toBe( 'rest_forbidden' ); - expect( error.data ).toEqual( { - status: 401, - } ); - // Re-authenticate & permanently delete this post - return authenticated.posts() - .id( id ) - .delete( { - force: true, - } ); - } ) - .then( ( response ) => { - expect( typeof response ).toBe( 'object' ); - // DELETE action returns the fully-deleted post object as .previous - expect( typeof response.previous ).toBe( 'object' ); - expect( response.previous.id ).toBe( id ); - // Query for the post, with auth: expect this to fail, since it is not - // just trashed but now deleted permanently - return authenticated.posts().id( id ); - } ) - .catch( ( error ) => { - expect( error.code ).toBe( 'rest_post_invalid_id' ); - expect( error.data ).toEqual( { - status: 404, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - }, 10000 ); - - it( 'can create a post with tags, categories and featured media', () => { - let id; - let mediaId; - const filePath = path.join( __dirname, 'assets/emilygarfield-untitled.jpg' ); - // Helper function - const ascById = ( a, b ) => a.id - b.id; - - const categories = []; - const tags = []; - const tagsAndPromises = Promise.all( [ - wp.categories().get(), - wp.tags().get(), - ] ) - .then( ( results ) => { - // Array to Object - return { - categories: results[ 0 ], - tags: results[ 1 ], - }; - } ); - const prom = tagsAndPromises - .then( ( results ) => { - // Pick two tags and a category to assign to our new post - tags.push( results.tags[ 1 ], results.tags[ 4 ] ); - categories.push( results.categories[ 3 ] ); - tags.sort( ascById ); - categories.sort( ascById ); - - // In this act we create the post, assigning the tags & categories - return authenticated.posts() - .create( { - title: 'New Post with Tags & Categories', - content: 'This post has a featured image, too', - status: 'publish', - categories: categories.map( cat => cat.id ), - tags: tags.map( tag => tag.id ), - } ); - } ) - .then( ( newPost ) => { - id = newPost.id; - // Now, upload the media we want to feature and associate with the new post - return authenticated.media() - .file( filePath ) - .create( { - post: id, - } ); - } ) - .then( ( media ) => { - mediaId = media.id; - // Assign the post the associated featured media - return authenticated.posts() - .id( id ) - .update( { - featured_media: mediaId, - } ); - } ) - .then( () => { - // Re-fetch the post with embedded content to validate all is set correctly - return wp.posts() - .id( id ) - .embed() - .get(); - } ) - .then( ( post ) => { - // Assert that the post got formed correctly - // Validate featured image - expect( post._embedded ).toHaveProperty( 'wp:featuredmedia' ); - expect( post._embedded[ 'wp:featuredmedia' ].length ).toBe( 1 ); - const media = post._embedded[ 'wp:featuredmedia' ][ 0 ]; - expect( media.id ).toBe( mediaId ); - expect( media.slug ).toMatch( /emilygarfield-untitled/ ); - expect( media.source_url ).toMatch( /emilygarfield-untitled(?:-\d*)?.jpg$/ ); - // Validate tags & categories - expect( post._embedded ).toHaveProperty( 'wp:term' ); - const terms = post._embedded[ 'wp:term' ]; - expect( terms.length ).toBe( 2 ); - // Validate all categories are present and accounted for - terms - .find( collection => collection[ 0 ].taxonomy === 'category' ) - .sort( ascById ) - .forEach( ( cat, idx ) => { - expect( cat.id ).toBe( categories[ idx ].id ); - expect( cat.name ).toBe( categories[ idx ].name ); - } ); - // Validate all tags are present and accounted for - terms - .find( collection => collection[ 0 ].taxonomy === 'post_tag' ) - .sort( ascById ) - .forEach( ( tag, idx ) => { - expect( tag.id ).toBe( tags[ idx ].id ); - expect( tag.name ).toBe( tags[ idx ].name ); - } ); - } ) - .then( () => { - // Clean up after ourselves: remove media - return authenticated.media() - .id( mediaId ) - .delete( { - force: true, - } ); - } ) - // Query for the media, with auth: expect this to fail, since it is gone - .then( () => authenticated.media().id( mediaId ) ) - .catch( ( error ) => { - expect( error.code ).toBe( 'rest_post_invalid_id' ); - expect( error.data ).toEqual( { - status: 404, - } ); - } ) - // Clean up after ourselves: remove post - .then( () => authenticated.posts() - .id( id ) - .delete( { - force: true, - } ) - ) - // Query for the post, with auth: expect this to fail, since it is gone - .then( () => authenticated.posts().id( id ) ) - .catch( ( error ) => { - expect( error.code ).toBe( 'rest_post_invalid_id' ); - expect( error.data ).toEqual( { - status: 404, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - }, 10000 ); - -} ); diff --git a/tests/integration/settings.js b/tests/integration/settings.js deleted file mode 100644 index b3bbc171..00000000 --- a/tests/integration/settings.js +++ /dev/null @@ -1,101 +0,0 @@ -'use strict'; - -const credentials = require( '../helpers/constants' ).credentials; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: settings()', ( transportName, WPAPI ) => { - let wp; - let authenticated; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - authenticated = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ).auth( credentials ); - } ); - - it( 'cannot be used to retrieve site settings unless authenticated', () => { - const prom = wp.settings() - .get() - .catch( ( err ) => { - expect( err.code ).toBe( 'rest_forbidden' ); - expect( err.data ).toEqual( { - status: 401, - } ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be used to retrieve a list of site settings when authenticated', () => { - const prom = authenticated.settings() - .get() - .then( ( settings ) => { - expect( typeof settings ).toBe( 'object' ); - - // Validate existence of all expected keys - expect( Object.keys( settings ).sort() ).toEqual( [ - 'date_format', - 'default_category', - 'default_comment_status', - 'default_ping_status', - 'default_post_format', - 'description', - 'email', - 'language', - 'posts_per_page', - 'start_of_week', - 'time_format', - 'timezone', - 'title', - 'url', - 'use_smilies', - ] ); - - // Spot check specific values - expect( settings.title ).toBe( 'WP-API Testbed' ); - expect( settings.description ).toBe( 'Just another WordPress site' ); - expect( settings.posts_per_page ).toBe( 10 ); - - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be used to update settings', () => { - const prom = authenticated.settings() - .get() - .then( ( settings ) => { - expect( settings.description ).toBe( 'Just another WordPress site' ); - return authenticated.settings() - .update( { - description: 'It\'s amazing what you\'ll find face to face', - } ); - } ) - // Initialize new request to see if changes persisted - .then( () => authenticated.settings().get() ) - .then( ( settings ) => { - expect( settings.description ).toBe( 'It's amazing what you'll find face to face' ); - // Reset to original value - return authenticated.settings() - .update( { - description: 'Just another WordPress site', - } ); - } ) - // Request one final time to validate value has been set back - .then( () => authenticated.settings().get() ) - .then( ( settings ) => { - expect( settings.description ).toBe( 'Just another WordPress site' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/integration/tags.js b/tests/integration/tags.js deleted file mode 100644 index d6cc1dbe..00000000 --- a/tests/integration/tags.js +++ /dev/null @@ -1,293 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../lib/constructors/wp-request.js' ); - -// Inspecting the names of the returned terms is an easy way to validate -// that the right page of results was returned -const getNames = require( '../helpers/get-prop' ).bind( null, 'name' ); - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -// Define some arrays to use ensuring the returned data is what we expect -// it to be (e.g. an array of the names from tags on the first page) -const expectedResults = { - names: { - page1: [ - '8BIT', - 'alignment', - 'Articles', - 'aside', - 'audio', - 'captions', - 'categories', - 'chat', - 'chattels', - 'cienaga', - ], - page2: [ - 'claycold', - 'Codex', - 'comments', - 'content', - 'crushing', - 'css', - 'depo', - 'dinarchy', - 'doolie', - 'dowork', - ], - pageLast: [ - 'trackbacks', - 'twitter', - 'unculpable', - 'Unseen', - 'video', - 'videopress', - 'withered brandnew', - 'WordPress', - 'wordpress.tv', - 'xanthopsia', - ], - }, -}; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: tags()', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - it( 'can be used to retrieve a collection of category terms', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( Array.isArray( tags ) ).toBe( true ); - expect( tags.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'retrieves the first 10 tags by default', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( Array.isArray( tags ) ).toBe( true ); - expect( tags.length ).toBe( 10 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - describe( 'paging properties', () => { - - it( 'are exposed as _paging on the response array', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( tags ).toHaveProperty( '_paging' ); - expect( typeof tags._paging ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of tags', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( tags._paging ).toHaveProperty( 'total' ); - expect( tags._paging.total ).toBe( 110 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'include the total number of pages available', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( tags._paging ).toHaveProperty( 'totalPages' ); - expect( tags._paging.totalPages ).toBe( 11 ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the next page as .next', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( tags._paging ).toHaveProperty( 'next' ); - expect( typeof tags._paging.next ).toBe( 'object' ); - expect( tags._paging.next ).toBeInstanceOf( WPRequest ); - expect( tags._paging.next._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/tags?page=2' ); - // Get last page & ensure "next" no longer appears - return wp.tags().page( tags._paging.totalPages ) - .get() - .then( ( tags ) => { - expect( tags._paging ).not.toHaveProperty( 'next' ); - expect( getNames( tags ) ).toEqual( expectedResults.names.pageLast ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the next page of results via .next', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - return tags._paging.next - .get() - .then( ( tags ) => { - expect( Array.isArray( tags ) ).toBe( true ); - expect( tags.length ).toBe( 10 ); - expect( getNames( tags ) ).toEqual( expectedResults.names.page2 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'provides a bound WPRequest for the previous page as .prev', () => { - const prom = wp.tags() - .get() - .then( ( tags ) => { - expect( tags._paging ).not.toHaveProperty( 'prev' ); - return tags._paging.next - .get() - .then( ( tags ) => { - expect( tags._paging ).toHaveProperty( 'prev' ); - expect( typeof tags._paging.prev ).toBe( 'object' ); - expect( tags._paging.prev ).toBeInstanceOf( WPRequest ); - expect( tags._paging.prev._options.endpoint ) - .toEqual( 'http://wpapi.local/wp-json/wp/v2/tags?page=1' ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'allows access to the previous page of results via .prev', () => { - const prom = wp.tags() - .page( 2 ) - .get() - .then( ( tags ) => { - expect( getNames( tags ) ).toEqual( expectedResults.names.page2 ); - return tags._paging.prev - .get() - .then( ( tags ) => { - expect( Array.isArray( tags ) ).toBe( true ); - expect( tags.length ).toBe( 10 ); - expect( getNames( tags ) ).toEqual( expectedResults.names.page1 ); - return SUCCESS; - } ); - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'id()', () => { - - it( 'can be used to access an individual tag term', () => { - let selectedTag; - const prom = wp.tags() - .get() - .then( ( tags ) => { - // Pick one of the tags - selectedTag = tags[ 3 ]; - // Query for that tag directly - return wp.tags().id( selectedTag.id ); - } ) - .then( ( tag ) => { - expect( typeof tag ).toBe( 'object' ); - expect( tag ).toHaveProperty( 'id' ); - expect( tag.id ).toBe( selectedTag.id ); - expect( tag ).toHaveProperty( 'slug' ); - expect( tag.slug ).toBe( selectedTag.slug ); - expect( tag ).toHaveProperty( 'taxonomy' ); - expect( tag.taxonomy ).toBe( 'post_tag' ); - expect( tag ).not.toHaveProperty( 'parent' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - - describe( 'search()', () => { - - it( 'can be used to retrieve a tag by slug', () => { - let selectedTag; - const prom = wp.tags() - .get() - .then( ( tags ) => { - // Pick one of the tags - selectedTag = tags[ 3 ]; - // Search for that tag by slug - return wp.tags().search( selectedTag.slug ); - } ) - .then( ( tags ) => { - expect( Array.isArray( tags ) ).toBe( true ); - expect( tags.length ).toBe( 1 ); - return tags[ 0 ]; - } ) - .then( ( tag ) => { - expect( typeof tag ).toBe( 'object' ); - expect( tag ).toHaveProperty( 'id' ); - expect( tag.id ).toBe( selectedTag.id ); - expect( tag ).toHaveProperty( 'slug' ); - expect( tag.slug ).toBe( selectedTag.slug ); - expect( tag ).toHaveProperty( 'taxonomy' ); - expect( tag.taxonomy ).toBe( 'post_tag' ); - expect( tag ).not.toHaveProperty( 'parent' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'returns all tags matching the provided search string', () => { - const prom = wp.tags() - .search( 'post' ) - .get() - .then( ( tags ) => { - expect( Array.isArray( tags ) ).toBe( true ); - expect( tags.length ).toBe( 2 ); - const slugs = tags.map( tag => tag.slug ).sort().join( ' ' ); - expect( slugs ).toBe( 'post post-formats' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be used to retrieve a tag by slug from a set of search results', () => { - const prom = wp.tags() - .search( 'post' ) - .get() - // Iterating over response of search is the best we can do until - // filtering for taxonomy term collections is reinstated - .then( tags => tags.find( tag => tag.slug === 'post' ) ) - .then( ( tag ) => { - expect( tag ).toHaveProperty( 'slug' ); - expect( tag.slug ).toBe( 'post' ); - expect( tag ).toHaveProperty( 'name' ); - expect( tag.name ).toBe( 'post' ); - expect( tag ).not.toHaveProperty( 'parent' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - } ); - -} ); diff --git a/tests/integration/taxonomies.js b/tests/integration/taxonomies.js deleted file mode 100644 index f3c04ca4..00000000 --- a/tests/integration/taxonomies.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict'; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: taxonomies()', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - it( 'can be used to retrieve a dictionary of registered taxonomies', () => { - const prom = wp.taxonomies() - .get() - .then( ( taxonomies ) => { - expect( typeof taxonomies ).toBe( 'object' ); - expect( taxonomies ).toHaveProperty( 'category' ); - expect( typeof taxonomies.category ).toBe( 'object' ); - expect( taxonomies ).toHaveProperty( 'post_tag' ); - expect( typeof taxonomies.post_tag ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be chained with a taxonomy() call to fetch the category taxonomy', () => { - const prom = wp.taxonomies() - .taxonomy( 'category' ) - .get() - .then( ( category ) => { - expect( typeof category ).toBe( 'object' ); - expect( category ).toHaveProperty( 'slug' ); - expect( category.slug ).toBe( 'category' ); - expect( category ).toHaveProperty( 'hierarchical' ); - expect( category.hierarchical ).toBe( true ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be chained with a taxonomy() call to fetch the post_tag taxonomy', () => { - const prom = wp.taxonomies() - .taxonomy( 'post_tag' ) - .get() - .then( ( tag ) => { - expect( typeof tag ).toBe( 'object' ); - expect( tag ).toHaveProperty( 'slug' ); - expect( tag.slug ).toBe( 'post_tag' ); - expect( tag ).toHaveProperty( 'hierarchical' ); - expect( tag.hierarchical ).toBe( false ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/integration/types.js b/tests/integration/types.js deleted file mode 100644 index a98fd166..00000000 --- a/tests/integration/types.js +++ /dev/null @@ -1,64 +0,0 @@ -'use strict'; - -// Variable to use as our "success token" in promise assertions -const SUCCESS = 'success'; - -describe.each( [ - [ 'wpapi/superagent', require( '../../superagent' ) ], - [ 'wpapi/fetch', require( '../../fetch' ) ], -] )( '%s: types()', ( transportName, WPAPI ) => { - let wp; - - beforeEach( () => { - wp = new WPAPI( { - endpoint: 'http://wpapi.local/wp-json', - } ); - } ); - - it( 'can be used to retrieve a dictionary of registered types', () => { - const prom = wp.types() - .get() - .then( ( types ) => { - expect( typeof types ).toBe( 'object' ); - expect( types ).toHaveProperty( 'post' ); - expect( typeof types.post ).toBe( 'object' ); - expect( types ).toHaveProperty( 'page' ); - expect( typeof types.page ).toBe( 'object' ); - expect( types ).toHaveProperty( 'attachment' ); - expect( typeof types.attachment ).toBe( 'object' ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be chained with a type() call to fetch the "post" type', () => { - const prom = wp.types() - .type( 'post' ) - .get() - .then( ( post ) => { - expect( typeof post ).toBe( 'object' ); - expect( post ).toHaveProperty( 'slug' ); - expect( post.slug ).toBe( 'post' ); - expect( post ).toHaveProperty( 'hierarchical' ); - expect( post.hierarchical ).toBe( false ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - - it( 'can be chained with a type() call to fetch the page type', () => { - const prom = wp.types() - .type( 'page' ) - .get() - .then( ( page ) => { - expect( typeof page ).toBe( 'object' ); - expect( page ).toHaveProperty( 'slug' ); - expect( page.slug ).toBe( 'page' ); - expect( page ).toHaveProperty( 'hierarchical' ); - expect( page.hierarchical ).toBe( true ); - return SUCCESS; - } ); - return expect( prom ).resolves.toBe( SUCCESS ); - } ); - -} ); diff --git a/tests/unit/build/scripts/posts-collection-route-definition.json b/tests/unit/build/scripts/posts-collection-route-definition.json deleted file mode 100644 index 02a50aea..00000000 --- a/tests/unit/build/scripts/posts-collection-route-definition.json +++ /dev/null @@ -1 +0,0 @@ -{"namespace":"wp/v2","methods":["GET","POST"],"endpoints":[{"methods":["GET"],"args":{"context":{"required":false,"default":"view","enum":["view","embed","edit"],"description":"Scope under which the request is made; determines fields present in response."},"page":{"required":false,"default":1,"description":"Current page of the collection."},"per_page":{"required":false,"default":10,"description":"Maximum number of items to be returned in result set."},"search":{"required":false,"description":"Limit results to those matching a string."},"after":{"required":false,"description":"Limit response to resources published after a given ISO8601 compliant date."},"author":{"required":false,"default":[],"description":"Limit result set to posts assigned to specific authors."},"author_exclude":{"required":false,"default":[],"description":"Ensure result set excludes posts assigned to specific authors."},"before":{"required":false,"description":"Limit response to resources published before a given ISO8601 compliant date."},"exclude":{"required":false,"default":[],"description":"Ensure result set excludes specific ids."},"include":{"required":false,"default":[],"description":"Limit result set to specific ids."},"offset":{"required":false,"description":"Offset the result set by a specific number of items."},"order":{"required":false,"default":"desc","enum":["asc","desc"],"description":"Order sort attribute ascending or descending."},"orderby":{"required":false,"default":"date","enum":["date","id","include","title","slug"],"description":"Sort collection by object attribute."},"slug":{"required":false,"description":"Limit result set to posts with a specific slug."},"status":{"required":false,"default":"publish","description":"Limit result set to posts assigned a specific status."},"filter":{"required":false,"description":"Use WP Query arguments to modify the response; private query vars require appropriate authorization."},"categories":{"required":false,"default":[],"description":"Limit result set to all items that have the specified term assigned in the categories taxonomy."},"tags":{"required":false,"default":[],"description":"Limit result set to all items that have the specified term assigned in the tags taxonomy."}}},{"methods":["POST"],"args":{"date":{"required":false,"description":"The date the object was published, in the site's timezone."},"date_gmt":{"required":false,"description":"The date the object was published, as GMT."},"password":{"required":false,"description":"A password to protect access to the post."},"slug":{"required":false,"description":"An alphanumeric identifier for the object unique to its type."},"status":{"required":false,"enum":["publish","future","draft","pending","private"],"description":"A named status for the object."},"title":{"required":false,"description":"The title for the object."},"content":{"required":false,"description":"The content for the object."},"author":{"required":false,"description":"The id for the author of the object."},"excerpt":{"required":false,"description":"The excerpt for the object."},"featured_media":{"required":false,"description":"The id of the featured media for the object."},"comment_status":{"required":false,"enum":["open","closed"],"description":"Whether or not comments are open on the object."},"ping_status":{"required":false,"enum":["open","closed"],"description":"Whether or not the object can be pinged."},"format":{"required":false,"enum":["standard","aside","chat","gallery","link","image","quote","status","video","audio"],"description":"The format for the object."},"sticky":{"required":false,"description":"Whether or not the object should be treated as sticky."},"categories":{"required":false,"description":"The terms assigned to the object in the category taxonomy."},"tags":{"required":false,"description":"The terms assigned to the object in the post_tag taxonomy."}}}],"_links":{"self":"http://wpapi.local/wp-json/wp/v2/posts"}} diff --git a/tests/unit/build/scripts/simplify-object.js b/tests/unit/build/scripts/simplify-object.js deleted file mode 100644 index f6723d0d..00000000 --- a/tests/unit/build/scripts/simplify-object.js +++ /dev/null @@ -1,117 +0,0 @@ -'use strict'; - -const simplifyObject = require( '../../../../build/scripts/simplify-object' ); - -const fullPostsCollectionRouteDefinition = require( './posts-collection-route-definition.json' ); - -describe( 'simplifyObject', () => { - - it( 'is a function', () => { - expect( typeof simplifyObject ).toBe( 'function' ); - } ); - - it( 'passes through strings without modification', () => { - expect( typeof simplifyObject( 'foo' ) ).toBe( 'string' ); - expect( simplifyObject( 'foo' ) ).toBe( 'foo' ); - } ); - - it( 'passes through numbers without modification', () => { - expect( typeof simplifyObject( 7 ) ).toBe( 'number' ); - expect( simplifyObject( 7 ) ).toBe( 7 ); - } ); - - it( 'passes through booleans without modification', () => { - expect( typeof simplifyObject( true ) ).toBe( 'boolean' ); - expect( simplifyObject( true ) ).toBe( true ); - } ); - - it( 'passes through arrays of simple values without modification', () => { - expect( Array.isArray( simplifyObject( [] ) ) ).toBe( true ); - expect( simplifyObject( [ 1, 2, 3 ] ) ).toEqual( [ 1, 2, 3 ] ); - expect( simplifyObject( [ 'a', 'b', 'c' ] ) ).toEqual( [ 'a', 'b', 'c' ] ); - expect( simplifyObject( [ true, false ] ) ).toEqual( [ true, false ] ); - } ); - - it( 'passes through most objects without modification', () => { - expect( simplifyObject( { - some: 'set', - of: 'basic', - nested: { - properties: [ 'of', 'no', { - particular: 'consequence', - } ], - nr: 7, - }, - } ) ).toEqual( { - some: 'set', - of: 'basic', - nested: { - properties: [ 'of', 'no', { - particular: 'consequence', - } ], - nr: 7, - }, - } ); - } ); - - it( 'strips out _links properties', () => { - expect( simplifyObject( { - some: 'object with a', - _links: { - prop: 'within it', - }, - } ) ).toEqual( { - some: 'object with a', - } ); - } ); - - it( 'removes unneeded keys from children of .args objects', () => { - expect( simplifyObject( { - args: { - context: { - required: false, - other: 'properties', - go: 'here', - }, - }, - } ) ).toEqual( { - args: { - context: {}, - }, - } ); - } ); - - it( 'properly transforms a full route definition object', () => { - expect( simplifyObject( fullPostsCollectionRouteDefinition ) ).toEqual( { - namespace: 'wp/v2', - methods: [ 'GET', 'POST' ], - endpoints: [ { - methods: [ 'GET' ], - args: { - context: {}, - page: {}, - per_page: {}, - search: {}, - after: {}, - author: {}, - author_exclude: {}, - before: {}, - exclude: {}, - include: {}, - offset: {}, - order: {}, - orderby: {}, - slug: {}, - status: {}, - filter: {}, - categories: {}, - tags: {}, - }, - }, { - methods: [ 'POST' ], - args: {}, - } ], - } ); - } ); - -} ); diff --git a/tests/unit/lib/bind-transport.js b/tests/unit/lib/bind-transport.js deleted file mode 100644 index 6dc32b89..00000000 --- a/tests/unit/lib/bind-transport.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -const bindTransport = require( '../../../lib/bind-transport' ); - -describe( 'bindTransport()', () => { - it( 'is a function', () => { - expect( bindTransport ).toBeInstanceOf( Function ); - } ); -} ); - -describe( 'Transport-bound WPAPI constructor', () => { - let transport; - let WPAPI; - - beforeEach( () => { - transport = { - get: jest.fn(), - }; - WPAPI = bindTransport( require( '../../../wpapi' ), transport ); - } ); - - it( 'has a .site() static method', () => { - expect( WPAPI ).toHaveProperty( 'site' ); - expect( typeof WPAPI.site ).toBe( 'function' ); - } ); - - it( 'returns instances of the expected constructor from WPAPI.site', () => { - const site = WPAPI.site( 'endpoint/url' ); - expect( site instanceof WPAPI ).toBe( true ); - expect( site._options.endpoint ).toBe( 'endpoint/url/' ); - expect( site._options.transport ).toBeDefined(); - expect( site._options.transport.get ).toBe( transport.get ); - } ); - -} ); diff --git a/tests/unit/lib/constructors/wp-request.js b/tests/unit/lib/constructors/wp-request.js deleted file mode 100644 index 7af28c91..00000000 --- a/tests/unit/lib/constructors/wp-request.js +++ /dev/null @@ -1,939 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../../../lib/constructors/wp-request' ); -const filterMixins = require( '../../../../lib/mixins/filters' ); -const checkMethodSupport = require( '../../../../lib/util/check-method-support' ); - -const getQueryStr = ( req ) => { - const query = req - ._renderQuery() - .replace( /^\?/, '' ); - return decodeURIComponent( query ); -}; - -describe( 'WPRequest', () => { - - let request; - - beforeEach( () => { - request = new WPRequest( { - endpoint: '/', - } ); - } ); - - describe( 'constructor', () => { - - it( 'should create a WPRequest instance', () => { - expect( request instanceof WPRequest ).toBe( true ); - } ); - - it( 'should set any passed-in options', () => { - request = new WPRequest( { - endpoint: '/custom-endpoint/', - } ); - expect( request.toString() ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should define a _supportedMethods array', () => { - const _supportedMethods = request._supportedMethods.sort().join( '|' ); - expect( _supportedMethods ).toBe( 'delete|get|head|post|put' ); - } ); - - } ); - - describe( '._renderQuery() [internal]', () => { - - beforeEach( () => { - Object.keys( filterMixins ).forEach( ( mixin ) => { - if ( ! request[ mixin ] ) { - request[ mixin ] = filterMixins[ mixin ]; - } - } ); - } ); - - it( 'properly parses taxonomy filters', () => { - request._taxonomyFilters = { - tag: [ 'clouds ', 'islands' ], - custom_tax: [ 7 ], - }; - const query = request._renderQuery(); - // Filters should be in alpha order, to support caching requests - expect( query ) - .toBe( '?filter%5Bcustom_tax%5D=7&filter%5Btag%5D=clouds%2Bislands' ); - } ); - - it( 'lower-cases taxonomy terms', () => { - request._taxonomyFilters = { - tag: [ 'Diamond-Dust' ], - }; - const query = request._renderQuery(); - expect( query ).toBe( '?filter%5Btag%5D=diamond-dust' ); - } ); - - it( 'properly parses regular filters', () => { - request._filters = { - post_status: 'publish', - s: 'Some search string', - }; - const query = request._renderQuery(); - expect( query ) - .toBe( '?filter%5Bpost_status%5D=publish&filter%5Bs%5D=Some%20search%20string' ); - } ); - - it( 'properly parses array filters', () => { - request._filters = { post__in: [ 0, 1 ] }; - const query = request._renderQuery(); - expect( query ) - .toBe( '?filter%5Bpost__in%5D%5B%5D=0&filter%5Bpost__in%5D%5B%5D=1' ); - } ); - - it( 'correctly merges taxonomy and regular filters & renders them in order', () => { - request._taxonomyFilters = { - cat: [ 7, 10 ], - }; - request._filters = { - name: 'some-slug', - }; - const query = request._renderQuery(); - // Filters should be in alpha order, to support caching requests - expect( query ).toBe( '?filter%5Bcat%5D=7%2B10&filter%5Bname%5D=some-slug' ); - } ); - - } ); - - describe( '.checkMethodSupport()', () => { - - it( 'should return true when called with a supported method', () => { - expect( checkMethodSupport( 'get', request ) ).toBe( true ); - } ); - - it( 'should throw an error when called with an unsupported method', () => { - request._supportedMethods = [ 'get' ]; - - expect( () => { - checkMethodSupport( 'post', request ); - } ).toThrow(); - } ); - - } ); - - describe( '.namespace()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'namespace' ); - expect( typeof request.namespace ).toBe( 'function' ); - } ); - - it( 'sets a value that is prepended to the path', () => { - request.namespace( 'ns' ); - expect( request._renderPath() ).toBe( 'ns' ); - } ); - - it( 'can accept & set a namespace in the (:domain/:version) format', () => { - request.namespace( 'ns/v3' ); - expect( request._renderPath() ).toBe( 'ns/v3' ); - } ); - - it( 'can be removed (to use the legacy api v1) with an empty string', () => { - request.namespace( 'windows/xp' ).namespace( '' ); - expect( request._renderPath() ).toBe( '' ); - } ); - - it( 'can be removed (to use the legacy api v1) by omitting arguments', () => { - request.namespace( 'wordpress/95' ).namespace(); - expect( request._renderPath() ).toBe( '' ); - } ); - - } ); - - describe( '.param()', () => { - - it( 'method exists', () => { - expect( request ).toHaveProperty( 'param' ); - expect( typeof request.param ).toBe( 'function' ); - } ); - - it( 'will have no effect if called without any arguments', () => { - request.param(); - expect( request._renderQuery() ).toBe( '' ); - } ); - - it( 'will set a query parameter value', () => { - request.param( 'key', 'value' ); - expect( request._renderQuery() ).toBe( '?key=value' ); - } ); - - it( 'will unset a query parameter value if called with empty string', () => { - request.param( 'key', 'value' ); - expect( request._renderQuery() ).toBe( '?key=value' ); - request.param( 'key', 'value' ); - request.param( 'key', '' ); - expect( request._renderQuery() ).toBe( '' ); - } ); - - it( 'will unset a query parameter value if called with null', () => { - request.param( 'key', 'value' ); - expect( request._renderQuery() ).toBe( '?key=value' ); - request.param( 'key', 'value' ); - request.param( 'key', null ); - expect( request._renderQuery() ).toBe( '' ); - } ); - - it( 'will have no effect if called with no value', () => { - request.param( 'key' ); - expect( request._renderQuery() ).toBe( '' ); - } ); - - it( 'will have no effect if called with an empty object', () => { - request.param( {} ); - expect( request._renderQuery() ).toBe( '' ); - } ); - - it( 'should set the internal _params hash', () => { - request.param( 'type', 'some_cpt' ); - expect( request._renderQuery() ).toBe( '?type=some_cpt' ); - request.param( 'context', 'edit' ); - expect( request._renderQuery() ).toBe( '?context=edit&type=some_cpt' ); - } ); - - it( 'should set multiple parameters by passing a hash object', () => { - request.param( { - page: 309, - context: 'view', - } ); - expect( request._renderQuery() ).toBe( '?context=view&page=309' ); - } ); - - it( 'should de-dupe & sort array values', () => { - request.param( 'type', [ 'post', 'page', 'post', 'page', 'cpt_item' ] ); - expect( request._renderQuery() ).toBe( '?type%5B%5D=cpt_item&type%5B%5D=page&type%5B%5D=post' ); - } ); - - } ); - - describe( '.param() convenience methods', () => { - - describe( '.context()', () => { - - beforeEach( () => { - request = new WPRequest( { - endpoint: '/', - } ); - } ); - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'context' ); - expect( typeof request.context ).toBe( 'function' ); - } ); - - it( 'wraps .param()', () => { - jest.spyOn( request, 'param' ); - request.context( 'view' ); - expect( request.param ).toHaveBeenCalledWith( 'context', 'view' ); - } ); - - it( 'should map to the "context=VALUE" query parameter', () => { - const path = request.context( 'edit' ).toString(); - expect( path ).toBe( '/?context=edit' ); - } ); - - it( 'should replace values when called multiple times', () => { - const path = request.context( 'edit' ).context( 'view' ).toString(); - expect( path ).toBe( '/?context=view' ); - } ); - - it( 'should provide a .edit() shortcut for .context( "edit" )', () => { - jest.spyOn( request, 'context' ); - request.edit(); - expect( request.context ).toHaveBeenCalledWith( 'edit' ); - expect( request.toString() ).toBe( '/?context=edit' ); - } ); - - } ); - - describe( '.embed()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'embed' ); - } ); - - it( 'is a function', () => { - expect( typeof request.embed ).toBe( 'function' ); - } ); - - it( 'should set the "_embed" parameter', () => { - request.embed(); - expect( request._params._embed ).toBe( true ); - } ); - - it( 'should be chainable', () => { - expect( request.embed() ).toBe( request ); - } ); - - } ); - - describe( '.page()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'page' ); - } ); - - it( 'is a function', () => { - expect( typeof request.page ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.page() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.page(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "page" query parameter when provided a value', () => { - const result = request.page( 7 ); - expect( getQueryStr( result ) ).toBe( 'page=7' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.page( 71 ).page( 2 ); - expect( getQueryStr( result ) ).toBe( 'page=2' ); - } ); - - } ); - - describe( '.perPage()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'perPage' ); - } ); - - it( 'is a function', () => { - expect( typeof request.perPage ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.perPage() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.perPage(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "per_page" query parameter when provided a value', () => { - const result = request.perPage( 7 ); - expect( getQueryStr( result ) ).toBe( 'per_page=7' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.perPage( 71 ).perPage( 2 ); - expect( getQueryStr( result ) ).toBe( 'per_page=2' ); - } ); - - } ); - - describe( '.offset()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'offset' ); - } ); - - it( 'is a function', () => { - expect( typeof request.offset ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.offset() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.offset(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "offset" query parameter when provided a value', () => { - const result = request.offset( 7 ); - expect( getQueryStr( result ) ).toBe( 'offset=7' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.offset( 71 ).offset( 2 ); - expect( getQueryStr( result ) ).toBe( 'offset=2' ); - } ); - - } ); - - describe( '.order()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'order' ); - } ); - - it( 'is a function', () => { - expect( typeof request.order ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.order() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.order(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "order" query parameter when provided a value', () => { - const result = request.order( 'asc' ); - expect( getQueryStr( result ) ).toBe( 'order=asc' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.order( 'asc' ).order( 'desc' ); - expect( getQueryStr( result ) ).toBe( 'order=desc' ); - } ); - - } ); - - describe( '.orderby()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'orderby' ); - } ); - - it( 'is a function', () => { - expect( typeof request.orderby ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.orderby() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.orderby(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "orderby" query parameter when provided a value', () => { - const result = request.orderby( 'title' ); - expect( getQueryStr( result ) ).toBe( 'orderby=title' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.orderby( 'title' ).orderby( 'slug' ); - expect( getQueryStr( result ) ).toBe( 'orderby=slug' ); - } ); - - } ); - - describe( '.search()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'search' ); - } ); - - it( 'is a function', () => { - expect( typeof request.search ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.search() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.search(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "search" query parameter when provided a value', () => { - const result = request.search( 'my search string' ); - expect( getQueryStr( result ) ).toBe( 'search=my search string' ); - } ); - - it( 'overwrites previously-set values on subsequent calls', () => { - const result = request.search( 'query' ).search( 'newquery' ); - expect( getQueryStr( result ) ).toBe( 'search=newquery' ); - } ); - - } ); - - describe( '.include()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'include' ); - } ); - - it( 'is a function', () => { - expect( typeof request.include ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.include() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.include(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "include" query parameter when provided a value', () => { - const result = request.include( 7 ); - expect( getQueryStr( result ) ).toBe( 'include=7' ); - } ); - - it( 'can set an array of "include" values', () => { - const result = request.include( [ 7, 41, 98 ] ); - expect( getQueryStr( result ) ).toBe( 'include[]=41&include[]=7&include[]=98' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.include( 71 ).include( 2 ); - expect( getQueryStr( result ) ).toBe( 'include=2' ); - } ); - - } ); - - describe( '.exclude()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'exclude' ); - } ); - - it( 'is a function', () => { - expect( typeof request.exclude ).toBe( 'function' ); - } ); - - it( 'should be chainable', () => { - expect( request.exclude() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.exclude(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "exclude" query parameter when provided a value', () => { - const result = request.exclude( 7 ); - expect( getQueryStr( result ) ).toBe( 'exclude=7' ); - } ); - - it( 'can set an array of "exclude" values', () => { - const result = request.exclude( [ 7, 41, 98 ] ); - expect( getQueryStr( result ) ).toBe( 'exclude[]=41&exclude[]=7&exclude[]=98' ); - } ); - - it( 'should be chainable and replace values when called multiple times', () => { - const result = request.exclude( 71 ).exclude( 2 ); - expect( getQueryStr( result ) ).toBe( 'exclude=2' ); - } ); - - } ); - - describe( '.slug()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'slug' ); - } ); - - it( 'is a function', () => { - expect( typeof request.slug ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( request.slug() ).toBe( request ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = request.slug(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "slug" query parameter when provided a value', () => { - const result = request.slug( 'bran-van' ); - expect( getQueryStr( result ) ).toBe( 'slug=bran-van' ); - } ); - - } ); - - } ); - - describe( '.auth()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'auth' ); - expect( typeof request.auth ).toBe( 'function' ); - } ); - - it( 'activates authentication for the request', () => { - expect( request._options ).not.toHaveProperty( 'auth' ); - request.auth(); - expect( request._options ).toHaveProperty( 'auth' ); - expect( request._options.auth ).toBe( true ); - } ); - - it( 'sets the username and password when provided in an object', () => { - expect( request._options ).not.toHaveProperty( 'username' ); - expect( request._options ).not.toHaveProperty( 'password' ); - request.auth( { - username: 'user', - password: 'pass', - } ); - expect( request._options ).toHaveProperty( 'username' ); - expect( request._options ).toHaveProperty( 'password' ); - expect( request._options.username ).toBe( 'user' ); - expect( request._options.password ).toBe( 'pass' ); - expect( request._options ).toHaveProperty( 'auth' ); - expect( request._options.auth ).toBe( true ); - } ); - - it( 'does not set username/password if they are not provided as string values', () => { - expect( request._options ).not.toHaveProperty( 'username' ); - expect( request._options ).not.toHaveProperty( 'password' ); - request.auth( { - username: 123, - password: false, - } ); - expect( request._options ).not.toHaveProperty( 'username' ); - expect( request._options ).not.toHaveProperty( 'password' ); - expect( request._options ).toHaveProperty( 'auth' ); - expect( request._options.auth ).toBe( true ); - } ); - - it( 'sets the nonce when provided in an object', () => { - expect( request._options ).not.toHaveProperty( 'nonce' ); - request.auth( { - nonce: 'nonceynonce', - } ); - expect( request._options ).toHaveProperty( 'nonce' ); - expect( request._options.nonce ).toBe( 'nonceynonce' ); - expect( request._options ).toHaveProperty( 'auth' ); - expect( request._options.auth ).toBe( true ); - } ); - - it( 'can update nonce credentials', () => { - request.auth( { - nonce: 'nonceynonce', - } ).auth( { - nonce: 'refreshednonce', - } ); - expect( request._options ).toHaveProperty( 'nonce' ); - expect( request._options.nonce ).toBe( 'refreshednonce' ); - expect( request._options ).toHaveProperty( 'auth' ); - expect( request._options.auth ).toBe( true ); - } ); - - } ); // auth - - describe( '.file()', () => { - - it( 'method exists', () => { - expect( request ).toHaveProperty( 'file' ); - expect( typeof request.file ).toBe( 'function' ); - } ); - - it( 'will have no effect if called without any arguments', () => { - request.file(); - expect( request._attachment ).toBeUndefined(); - } ); - - it( 'will set a file path to upload', () => { - request.file( '/some/file.jpg' ); - expect( request._attachment ).toBe( '/some/file.jpg' ); - } ); - - it( 'will replace previously-set file paths if called multiple times', () => { - request.file( '/some/file.jpg' ).file( '/some/other/file.jpg' ); - expect( request._attachment ).toBe( '/some/other/file.jpg' ); - } ); - - it( 'will clear out previously-set paths if called again without any arguments', () => { - request.file( '/some/file.jpg' ).file(); - expect( request._attachment ).toBeUndefined(); - } ); - - it( 'will set an attachment name to use for the provided file', () => { - request.file( '/some/file.jpg', 'cat_picture.jpg' ); - expect( request._attachmentName ).toBe( 'cat_picture.jpg' ); - } ); - - it( 'will clear out previously-set name if called again without a name', () => { - request.file( '/some/file.jpg', 'cat_picture.jpg' ).file( '/some/other/file.jpg' ); - expect( request._attachmentName ).toBeUndefined(); - } ); - - } ); - - describe( '.setHeaders()', () => { - - it( 'method exists', () => { - expect( request ).toHaveProperty( 'setHeaders' ); - expect( typeof request.setHeaders ).toBe( 'function' ); - } ); - - it( 'will have no effect if called without any arguments', () => { - request.setHeaders(); - expect( request._options.headers ).toEqual( {} ); - } ); - - it( 'will set a header key/value pair', () => { - request.setHeaders( 'Authorization', 'Bearer sometoken' ); - expect( request._options.headers ).toEqual( { - Authorization: 'Bearer sometoken', - } ); - } ); - - it( 'will replace an existing header key/value pair', () => { - request - .setHeaders( 'Authorization', 'Bearer sometoken' ) - .setHeaders( 'Authorization', 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' ); - expect( request._options.headers ).toEqual( { - Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', - } ); - } ); - - it( 'will set multiple header key/value pairs with chained calls', () => { - request - .setHeaders( 'Accept-Language', 'en-US' ) - .setHeaders( 'Authorization', 'Bearer sometoken' ); - expect( request._options.headers ).toEqual( { - 'Accept-Language': 'en-US', - Authorization: 'Bearer sometoken', - } ); - } ); - - it( 'will set multiple header key/value pairs when passed an object', () => { - request.setHeaders( { - 'Accept-Language': 'en-US', - Authorization: 'Bearer sometoken', - } ); - expect( request._options.headers ).toEqual( { - 'Accept-Language': 'en-US', - Authorization: 'Bearer sometoken', - } ); - } ); - - it( 'will replace multiple existing header key/value pairs when passed an object', () => { - request - .setHeaders( { - 'Accept-Language': 'en-US', - Authorization: 'Bearer sometoken', - } ) - .setHeaders( { - 'Accept-Language': 'pt-BR', - Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', - } ); - expect( request._options.headers ).toEqual( { - 'Accept-Language': 'pt-BR', - Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', - } ); - } ); - - it( 'inherits headers from the constructor options object', () => { - request = new WPRequest( { - endpoint: '/', - headers: { - 'Accept-Language': 'pt-BR', - }, - } ); - expect( request._options.headers ).toEqual( { - 'Accept-Language': 'pt-BR', - } ); - } ); - - } ); - - describe( '.toString()', () => { - - beforeEach( () => { - request = new WPRequest( { - endpoint: 'http://blogoblog.com/wp-json', - } ); - } ); - - it( 'renders the URL to a string', () => { - const str = request.param( 'a', 7 ).param( 'b', [ 1, 2 ] ).toString(); - expect( str ).toBe( 'http://blogoblog.com/wp-json?a=7&b%5B%5D=1&b%5B%5D=2' ); - } ); - - it( 'exhibits normal toString() behavior via coercion', () => { - const str = '' + request.param( 'a', 7 ).param( 'b', [ 1, 2 ] ); - expect( str ).toBe( 'http://blogoblog.com/wp-json?a=7&b%5B%5D=1&b%5B%5D=2' ); - } ); - - it( 'correctly merges query strings for "plain permalinks" endpoints', () => { - request = new WPRequest( { - endpoint: 'https://blogoblog.com?rest_route=/', - } ); - const str = request.param( 'a', 7 ).param( 'b', [ 1, 2 ] ).toString(); - expect( str ).toBe( 'https://blogoblog.com?rest_route=/&a=7&b%5B%5D=1&b%5B%5D=2' ); - } ); - - } ); - - describe( '.setPathPart()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'setPathPart' ); - } ); - - it( 'is a function', () => { - expect( typeof request.setPathPart ).toBe( 'function' ); - } ); - - it( 'is chainable', () => { - expect( request.setPathPart() ).toBe( request ); - } ); - - it( 'sets a path part', () => { - request.setPathPart( 0, 'foo' ); - expect( request.toString() ).toBe( '/foo' ); - } ); - - it( 'sets multiple path parts', () => { - request.setPathPart( 0, 'foo' ).setPathPart( 1, 'bar' ); - expect( request.toString() ).toBe( '/foo/bar' ); - } ); - - it( 'sets multiple non-consecutive path parts', () => { - request.setPathPart( 0, 'foo' ).setPathPart( 2, 'baz' ); - expect( request.toString() ).toBe( '/foo/baz' ); - } ); - - it( 'throws an error if called multiple times for the same level', () => { - expect( () => { - request.setPathPart( 0, 'foo' ).setPathPart( 0, 'bar' ); - } ).toThrow( 'Cannot overwrite value foo' ); - } ); - - } ); - - describe( '.validatePath()', () => { - - it( 'is defined', () => { - expect( request ).toHaveProperty( 'validatePath' ); - } ); - - it( 'is a function', () => { - expect( typeof request.validatePath ).toBe( 'function' ); - } ); - - it( 'is chainable', () => { - expect( request.validatePath() ).toBe( request ); - } ); - - it( 'is called by toString()', () => { - jest.spyOn( request, 'validatePath' ); - request.toString(); - expect( request.validatePath ).toHaveBeenCalled(); - } ); - - it( 'allows any sequence of path parts if no _levels are specified', () => { - delete request._levels; - expect( () => { - request - .setPathPart( 0, 'foo' ) - .setPathPart( 4, 'bar' ) - .setPathPart( 2, 'baz' ) - .validatePath(); - } ).not.toThrow(); - expect( request.toString() ).toBe( '/foo/baz/bar' ); - } ); - - it( 'allows omitted _levels so long as there are no gaps', () => { - request._levels = { - '0': [ { component: 'posts' } ], - '1': [ { component: '(?P[\\d]+)' } ], - }; - expect( () => { - request.setPathPart( 0, 'posts' ).validatePath(); - } ).not.toThrow(); - expect( request.toString() ).toBe( '/posts' ); - } ); - - it( 'allows any value for a level if no validate function is specified', () => { - request._levels = { - '0': [ { component: '(?P[\\d]+)' } ], - }; - expect( () => { - request.setPathPart( 0, 'foo' ).validatePath(); - } ).not.toThrow(); - expect( request.toString() ).toBe( '/foo' ); - } ); - - it( 'requires a level to conform to a validate function, when provided', () => { - request._levels = { - '0': [ { - component: '(?P[\\d]+)', - validate: val => /^[\d]+$/.test( val ), - } ], - }; - expect( () => { - request.setPathPart( 0, 'foo' ).validatePath(); - } ).toThrow( 'foo does not match (?P[\\d]+)' ); - } ); - - it( 'allows any value for a level that passes a validate function, when provided', () => { - request._levels = { - '0': [ { - component: '(?P[\\d]+)', - validate: val => /^[\d]+$/.test( val ), - } ], - }; - expect( () => { - request.setPathPart( 0, '42' ).validatePath(); - } ).not.toThrow(); - expect( request.toString() ).toBe( '/42' ); - } ); - - it( 'requires a level to conform to any of several validate functions when provided', () => { - request._levels = { - '0': [ { - component: '(?P[\\d]+)', - validate: val => /^[\d]+$/.test( val ), - }, { - component: 'posts', - validate: val => val === 'posts', - }, { - component: 'pages', - validate: val => val === 'pages', - } ], - }; - expect( () => { - request.setPathPart( 0, 'foo' ).validatePath(); - } ).toThrow( 'foo does not match any of (?P[\\d]+), posts, pages' ); - } ); - - it( 'allows any value for a level that passes any of the available validate functions', () => { - request._levels = { - '0': [ { - component: '(?P[\\d]+)', - validate: val => /^[\d]+$/.test( val ), - }, { - component: 'posts', - validate: val => val === 'posts', - }, { - component: 'pages', - validate: val => val === 'pages', - } ], - }; - expect( () => { - request.setPathPart( 0, 'posts' ).validatePath(); - } ).not.toThrow(); - expect( request.toString() ).toBe( '/posts' ); - } ); - - it( 'catches missing path parts if _levels are specified', () => { - request._levels = { - '0': [ { component: '(?P[\\d]+)' } ], - '1': [ { component: 'revisions' } ], - // '2': [ { component: '(?P[\\d]+)' } ] - }; - expect( () => { - request.setPathPart( 1, 'revisions' ).validatePath(); - } ).toThrow( 'Incomplete URL! Missing component: / ??? /revisions' ); - } ); - - } ); - -} ); diff --git a/tests/unit/lib/mixins/filters.js b/tests/unit/lib/mixins/filters.js deleted file mode 100644 index f51f2fe4..00000000 --- a/tests/unit/lib/mixins/filters.js +++ /dev/null @@ -1,390 +0,0 @@ -'use strict'; - -const filterMixins = require( '../../../../lib/mixins/filters' ); -const WPRequest = require( '../../../../lib/constructors/wp-request' ); - -const getQueryStr = ( req ) => { - const query = req - ._renderQuery() - .replace( /^\?/, '' ); - return decodeURIComponent( query ); -}; - -describe( 'mixins: filter', () => { - let Req; - let req; - - beforeEach( () => { - Req = class extends WPRequest {}; - req = new Req(); - } ); - - describe( '.filter()', () => { - - beforeEach( () => { - Req.prototype.filter = filterMixins.filter; - } ); - - it( 'mixin method is defined', () => { - expect( filterMixins ).toHaveProperty( 'filter' ); - } ); - - it( 'is a function', () => { - expect( typeof filterMixins.filter ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.filter() ).toBe( req ); - } ); - - it( 'will nave no effect if called with no filter value', () => { - const result = req.filter( 'a' ); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the filter query parameter on a request instance', () => { - const result = req.filter( 'a', 'b' ); - expect( getQueryStr( result ) ).toBe( 'filter[a]=b' ); - } ); - - it( 'can set multiple filters on the request', () => { - const result = req.filter( 'a', 'b' ).filter( 'c', 'd' ); - expect( getQueryStr( result ) ).toBe( 'filter[a]=b&filter[c]=d' ); - } ); - - it( 'will overwrite previously-set filter values', () => { - const result = req.filter( 'a', 'b' ).filter( 'a', 'd' ); - expect( getQueryStr( result ) ).toBe( 'filter[a]=d' ); - } ); - - it( 'will unset a filter if called with an empty string', () => { - const result = req.filter( 'a', 'b' ).filter( 'a', '' ); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'will unset a filter if called with null', () => { - const result = req.filter( 'a', 'b' ).filter( 'a', null ); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'can set multiple filters in one call when passed an object', () => { - const result = req.filter( { - a: 'b', - c: 'd', - e: 'f', - } ); - expect( getQueryStr( result ) ).toBe( 'filter[a]=b&filter[c]=d&filter[e]=f' ); - } ); - - it( 'can set multiple filters on the request when passed an object', () => { - const result = req - .filter( { - a: 'b', - c: 'd', - } ) - .filter( { - e: 'f', - } ); - expect( getQueryStr( result ) ).toBe( 'filter[a]=b&filter[c]=d&filter[e]=f' ); - } ); - - it( 'will overwrite multiple previously-set filter values when passed an object', () => { - const result = req - .filter( { - a: 'b', - c: 'd', - e: 'f', - } ) - .filter( { - a: 'g', - c: 'h', - i: 'j', - } ); - expect( getQueryStr( result ) ).toBe( 'filter[a]=g&filter[c]=h&filter[e]=f&filter[i]=j' ); - } ); - - } ); - - describe( 'taxonomy()', () => { - - beforeEach( () => { - Req.prototype.taxonomy = filterMixins.taxonomy; - } ); - - it( 'mixin is defined', () => { - expect( filterMixins ).toHaveProperty( 'taxonomy' ); - } ); - - it( 'is a function', () => { - expect( typeof filterMixins.taxonomy ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.taxonomy( 'tag', 'foo' ) ).toBe( req ); - } ); - - describe( 'argument type check errors', () => { - - it( 'errors if no term is provided', () => { - expect( () => { req.taxonomy( 'tag' ); } ).toThrow(); - } ); - - it( 'does not error if the term is a string', () => { - expect( () => { req.taxonomy( 'tag', 'cat' ); } ).not.toThrow(); - } ); - - it( 'does not error if the term is an array of strings', () => { - expect( () => { req.taxonomy( 'tag', [ 'cat', 'dog' ] ); } ).not.toThrow(); - } ); - - it( 'does not error if term is a number', () => { - expect( () => { req.taxonomy( 'cat', 7 ); } ).not.toThrow(); - } ); - - it( 'does not error if term is an array of numbers', () => { - expect( () => { req.taxonomy( 'cat', [ 7, 11 ] ); } ).not.toThrow(); - } ); - - it( 'errors if the term is null', () => { - expect( () => { req.taxonomy( 'tag', null ); } ).toThrow(); - } ); - - it( 'errors if the term is a boolean', () => { - expect( () => { req.taxonomy( 'tag', true ); } ).toThrow(); - expect( () => { req.taxonomy( 'tag', false ); } ).toThrow(); - } ); - - it( 'errors if the term is a Date', () => { - expect( () => { req.taxonomy( 'tag', new Date() ); } ).toThrow(); - } ); - - it( 'errors if the term is an object', () => { - expect( () => { req.taxonomy( 'tag', {} ); } ).toThrow(); - } ); - - it( 'errors if the term is an array of types other than strings or numbers', () => { - expect( () => { req.taxonomy( 'tag', [ null ] ); } ).toThrow(); - } ); - - it( 'errors if the term is not all strings or numbers', () => { - expect( () => { req.taxonomy( 'tag', [ 'cat', null ] ); } ).toThrow(); - expect( () => { req.taxonomy( 'cat', [ 7, null ] ); } ).toThrow(); - expect( () => { req.taxonomy( 'cat', [ 'foo', 7 ] ); } ).toThrow(); - } ); - - } ); - - describe( 'filter name aliasing behavior', () => { - - it( 'sets the "category_name" filter for categories where the term is a string', () => { - const result = req.taxonomy( 'category', 'str' ); - expect( getQueryStr( result ) ).toBe( 'filter[category_name]=str' ); - } ); - - it( 'sets the "cat" filter for categories where the term is a number', () => { - const result = req.taxonomy( 'category', 7 ); - expect( getQueryStr( result ) ).toBe( 'filter[cat]=7' ); - } ); - - it( 'sets the "tag" filter if the taxonomy is "post_tag"', () => { - const result = req.taxonomy( 'post_tag', 'sometag' ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=sometag' ); - } ); - - } ); - - describe( 'filter value setting behavior', () => { - - it( 'de-duplicates taxonomy terms (will only set a term once)', () => { - const result = req.taxonomy( 'tag', 'cat' ).taxonomy( 'tag', 'cat' ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=cat' ); - } ); - - it( 'de-dupes the taxonomy list when called with an array', () => { - req.taxonomy( 'post_tag', [ - 'disclosure', - 'alunageorge', - 'disclosure', - 'lorde', - 'lorde', - 'clean-bandit', - ] ); - expect( req._taxonomyFilters ).toEqual( { - tag: [ 'alunageorge', 'clean-bandit', 'disclosure', 'lorde' ], - } ); - } ); - - it( 'supports setting an array of string terms', () => { - // TODO: Multiple terms may be deprecated by API! - const result = req.taxonomy( 'tag', [ 'a', 'b' ] ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=a+b' ); - } ); - - it( 'supports setting an array of numeric terms', () => { - // TODO: Multiple terms may be deprecated by API! - const result = req.taxonomy( 'tag', [ 1, 2 ] ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=1+2' ); - } ); - - it( 'does not overwrite previously-specified terms on subsequent calls', () => { - // TODO: Multiple terms may be deprecated by API! - const result = req.taxonomy( 'tag', 'a' ).taxonomy( 'tag', [ 'b' ] ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=a+b' ); - } ); - - it( 'sorts provided terms', () => { - const result = req.taxonomy( 'tag', 'z' ).taxonomy( 'tag', 'a' ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=a+z' ); - } ); - - } ); - - } ); - - describe( '.path()', () => { - - beforeEach( () => { - Req.prototype.path = filterMixins.path; - } ); - - it( 'mixin is defined', () => { - expect( filterMixins ).toHaveProperty( 'path' ); - } ); - - it( 'is a function', () => { - expect( typeof filterMixins.path ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.path( 'tag', 'foo' ) ).toBe( req ); - } ); - - it( 'should create the URL for retrieving a post by path', () => { - const path = req.path( 'nested/page' ); - expect( getQueryStr( path ) ).toBe( 'filter[pagename]=nested/page' ); - } ); - - } ); - - describe( 'date filters', () => { - - describe( 'year()', () => { - - beforeEach( () => { - // By only applying .year and not .filter, we implicitly test that - // .year does not depend on the .filter mixin having been added - Req.prototype.year = filterMixins.year; - } ); - - it( 'mixin is defined', () => { - expect( filterMixins ).toHaveProperty( 'year' ); - } ); - - it( 'is a function', () => { - expect( typeof filterMixins.year ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.year( 'foo' ) ).toBe( req ); - } ); - - it( 'is an alias for .filter( "year", ... )', () => { - Req.prototype.filter = filterMixins.filter; - const result1 = ( new Req() ).year( '2015' ); - const result2 = ( new Req() ).filter( 'year', '2015' ); - expect( getQueryStr( result1 ) ).toBe( getQueryStr( result2 ) ); - } ); - - it( 'sets the "year" filter', () => { - const result = req.year( 'str' ); - expect( getQueryStr( result ) ).toBe( 'filter[year]=str' ); - } ); - - } ); - - describe( 'month()', () => { - - beforeEach( () => { - // By only applying .month and not .filter, we implicitly test that - // .month does not depend on the .filter mixin having been added - Req.prototype.month = filterMixins.month; - } ); - - it( 'mixin is defined', () => { - expect( filterMixins ).toHaveProperty( 'month' ); - } ); - - it( 'is a function', () => { - expect( typeof filterMixins.month ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.month( 'foo' ) ).toBe( req ); - } ); - - it( 'is an alias for .filter( "monthnum", ... )', () => { - Req.prototype.filter = filterMixins.filter; - const result1 = ( new Req() ).month( 7 ); - const result2 = ( new Req() ).filter( 'monthnum', 7 ); - expect( getQueryStr( result1 ) ).toBe( getQueryStr( result2 ) ); - } ); - - it( 'sets the "monthnum" filter', () => { - const result = req.month( 1 ); - expect( getQueryStr( result ) ).toBe( 'filter[monthnum]=1' ); - } ); - - it( 'converts named months into their numeric monthnum equivalent', () => { - const result = req.month( 'March' ); - expect( getQueryStr( result ) ).toBe( 'filter[monthnum]=3' ); - } ); - - it( 'returns without setting any filter if an invalid month string is provided', () => { - const result = req.month( 'Not a month' ).toString(); - expect( result.match( /filter/ ) ).toBe( null ); - } ); - - it( 'returns without setting any filter if an invalid argument is provided', () => { - const result = req.month( [ 'arrrr', 'i', 'be', 'an', 'array!' ] ).toString(); - expect( result.match( /filter/ ) ).toBe( null ); - } ); - - } ); - - describe( 'day()', () => { - - beforeEach( () => { - // By only applying .day and not .filter, we implicitly test that - // .day does not depend on the .filter mixin having been added - Req.prototype.day = filterMixins.day; - } ); - - it( 'mixin is defined', () => { - expect( filterMixins ).toHaveProperty( 'day' ); - } ); - - it( 'is a function', () => { - expect( typeof filterMixins.day ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.day( 'foo' ) ).toBe( req ); - } ); - - it( 'is an alias for .filter( "day", ... )', () => { - Req.prototype.filter = filterMixins.filter; - const result1 = ( new Req() ).day( '2015' ); - const result2 = ( new Req() ).filter( 'day', '2015' ); - expect( getQueryStr( result1 ) ).toBe( getQueryStr( result2 ) ); - } ); - - it( 'sets the "day" filter', () => { - const result = req.day( 'str' ); - expect( getQueryStr( result ) ).toBe( 'filter[day]=str' ); - } ); - - } ); - - } ); - -} ); diff --git a/tests/unit/lib/mixins/parameters.js b/tests/unit/lib/mixins/parameters.js deleted file mode 100644 index bbe0d9e3..00000000 --- a/tests/unit/lib/mixins/parameters.js +++ /dev/null @@ -1,552 +0,0 @@ -'use strict'; - -const parameterMixins = require( '../../../../lib/mixins/parameters' ); -const WPRequest = require( '../../../../lib/constructors/wp-request' ); - -const getQueryStr = ( req ) => { - const query = req - ._renderQuery() - .replace( /^\?/, '' ); - return decodeURIComponent( query ); -}; - -describe( 'mixins: parameters', () => { - let Req; - let req; - - beforeEach( () => { - Req = class extends WPRequest {}; - req = new Req(); - } ); - - describe( 'date parameters', () => { - - describe( '.before()', () => { - - beforeEach( () => { - Req.prototype.before = parameterMixins.before; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'before' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.before ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.before( '1933-11-01' ) ).toBe( req ); - } ); - - it( 'throws an error when called with a missing or invalid time', () => { - expect( () => { - req.before(); - } ).toThrow( 'Invalid time value' ); - } ); - - it( 'sets the "before" query parameter as an ISO 8601 Date', () => { - const result = req.before( '2016-07-01' ); - expect( getQueryStr( result ) ).toBe( 'before=2016-07-01T00:00:00.000Z' ); - } ); - - it( 'sets the "before" query parameter when provided a Date object', () => { - const date = new Date( 1986, 2, 22 ); - const result = req.before( date ); - // use .match and regex to avoid time zone-induced false negatives - expect( getQueryStr( result ) ).toMatch( /^before=1986-03-22T\d{2}:\d{2}:\d{2}.\d{3}Z$/ ); - } ); - - } ); - - describe( '.after()', () => { - - beforeEach( () => { - Req.prototype.after = parameterMixins.after; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'after' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.after ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.after( '1992-04-22' ) ).toBe( req ); - } ); - - it( 'throws an error when called with a missing or invalid time', () => { - expect( () => { - req.after(); - } ).toThrow( 'Invalid time value' ); - } ); - - it( 'sets the "after" query parameter when provided a value', () => { - const result = req.after( '2016-03-22' ); - expect( getQueryStr( result ) ).toBe( 'after=2016-03-22T00:00:00.000Z' ); - } ); - - it( 'sets the "after" query parameter when provided a Date object', () => { - const date = new Date( 1987, 11, 7 ); - const result = req.after( date ); - // use .match and regex to avoid time zone-induced false negatives - expect( getQueryStr( result ) ).toMatch( /^after=1987-12-07T\d{2}:\d{2}:\d{2}.\d{3}Z$/ ); - } ); - - } ); - - } ); - - describe( '.author()', () => { - - beforeEach( () => { - Req.prototype.author = parameterMixins.author; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'author' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.author ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.author( 1 ) ).toBe( req ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = req.author(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'throws an error when called with a non-string, non-numeric value', () => { - expect( () => { req.author( {} ); } ).toThrow(); - } ); - - it( 'sets the "author" query parameter when provided a numeric value', () => { - const result = req.author( 1138 ); - expect( getQueryStr( result ) ).toBe( 'author=1138' ); - } ); - - it( 'sets the "author_name" filter when provided a string value', () => { - const result = req.author( 'jamesagarfield' ); - expect( getQueryStr( result ) ).toBe( 'filter[author_name]=jamesagarfield' ); - } ); - - it( 'is chainable, and replaces author_name values on subsequent calls', () => { - const result = req.author( 'fforde' ).author( 'bronte' ); - expect( result ).toBe( req ); - expect( getQueryStr( result ) ).toBe( 'filter[author_name]=bronte' ); - } ); - - it( 'is chainable, and replaces author ID values on subsequent calls', () => { - const result = req.author( 1847 ); - expect( getQueryStr( result ) ).toBe( 'author=1847' ); - } ); - - it( 'unsets author when called with an empty string', () => { - const result = req.author( 'jorge-luis-borges' ).author( '' ); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'unsets author when called with null', () => { - const result = req.author( 7 ).author( null ); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'unsets author parameter when called with author name string', () => { - const result = req.author( 7 ).author( 'haruki-murakami' ); - expect( getQueryStr( result ) ).toBe( 'filter[author_name]=haruki-murakami' ); - } ); - - it( 'unsets author name filter when called with numeric author id', () => { - const result = req.author( 'haruki-murakami' ).author( 7 ); - expect( getQueryStr( result ) ).toBe( 'author=7' ); - } ); - - } ); - - describe( '.parent()', () => { - - beforeEach( () => { - Req.prototype.parent = parameterMixins.parent; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'parent' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.parent ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.parent() ).toBe( req ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = req.parent(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "parent" query parameter when provided a value', () => { - const result = req.parent( 42 ); - expect( getQueryStr( result ) ).toBe( 'parent=42' ); - } ); - - it( 'replaces values on subsequent calls', () => { - const result = req.parent( 42 ).parent( 2501 ); - expect( getQueryStr( result ) ).toBe( 'parent=2501' ); - } ); - - it( 'can pass an array of parent values', () => { - const result = req.parent( [ 42, 2501 ] ); - expect( getQueryStr( result ) ).toBe( 'parent[]=2501&parent[]=42' ); - } ); - - } ); - - describe( '.post()', () => { - - beforeEach( () => { - Req.prototype.post = parameterMixins.post; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'post' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.post ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.post() ).toBe( req ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = req.post(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "post" query parameter when provided a value', () => { - const result = req.post( 3263827 ); - expect( getQueryStr( result ) ).toBe( 'post=3263827' ); - } ); - - it( 'overwrites previously-set values on subsequent calls', () => { - const result = req.post( 1138 ).post( 2501 ); - expect( getQueryStr( result ) ).toBe( 'post=2501' ); - } ); - - } ); - - describe( '.password()', () => { - - beforeEach( () => { - Req.prototype.password = parameterMixins.password; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'password' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.password ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.password() ).toBe( req ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = req.password(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "password" query parameter when provided a value', () => { - const result = req.password( 'correct horse battery staple' ); - expect( getQueryStr( result ) ).toBe( 'password=correct horse battery staple' ); - } ); - - it( 'overwrites previously-set values on subsequent calls', () => { - const result = req.password( 'correct horse' ).password( 'battery staple' ); - expect( getQueryStr( result ) ).toBe( 'password=battery staple' ); - } ); - - } ); - - describe( '.status()', () => { - - beforeEach( () => { - Req.prototype.status = parameterMixins.status; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'status' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.status ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.status( 'publish' ) ).toBe( req ); - } ); - - it( 'sets the "status" query parameter when provided a value', () => { - const result = req.status( 'future' ); - expect( getQueryStr( result ) ).toBe( 'status=future' ); - } ); - - it( 'sets an array of "status" query values when provided an array of strings', () => { - const result = req.status( [ 'future', 'draft' ] ); - expect( getQueryStr( result ) ).toBe( 'status[]=draft&status[]=future' ); - } ); - - } ); - - describe( '.sticky()', () => { - - beforeEach( () => { - Req.prototype.sticky = parameterMixins.sticky; - } ); - - it( 'mixin method is defined', () => { - expect( parameterMixins ).toHaveProperty( 'sticky' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.sticky ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.sticky() ).toBe( req ); - } ); - - it( 'has no effect when called with no argument', () => { - const result = req.sticky(); - expect( getQueryStr( result ) ).toBe( '' ); - } ); - - it( 'sets the "sticky" query parameter when provided a value', () => { - const result = req.sticky( true ); - expect( getQueryStr( result ) ).toBe( 'sticky=true' ); - } ); - - it( 'overwrites previously-set values on subsequent calls', () => { - const result = req.sticky( 1 ).sticky( 0 ); - expect( getQueryStr( result ) ).toBe( 'sticky=0' ); - } ); - - } ); - - describe( 'categories()', () => { - - beforeEach( () => { - Req.prototype.categories = parameterMixins.categories; - } ); - - it( 'mixin is defined', () => { - expect( parameterMixins ).toHaveProperty( 'categories' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.categories ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.categories( 7 ) ).toBe( req ); - } ); - - it( 'sets the "categories" parameter for a single category ID', () => { - const result = req.categories( 7 ); - expect( getQueryStr( result ) ).toBe( 'categories=7' ); - } ); - - it( 'sets the "categories" parameter for multiple category IDs', () => { - const result = req.categories( [ 7, 13 ] ); - expect( getQueryStr( result ) ).toBe( 'categories[]=13&categories[]=7' ); - } ); - - } ); - - describe( 'category()', () => { - - beforeEach( () => { - Req.prototype.category = parameterMixins.category; - } ); - - it( 'mixin is defined', () => { - expect( parameterMixins ).toHaveProperty( 'category' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.category ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.category( 'foo' ) ).toBe( req ); - } ); - - it( 'sets the "categories" parameter for a single category ID', () => { - const result = req.category( 7 ); - expect( getQueryStr( result ) ).toBe( 'categories=7' ); - } ); - - it( 'sets the "categories" parameter for multiple category IDs', () => { - const result = req.category( [ 7, 13 ] ); - expect( getQueryStr( result ) ).toBe( 'categories[]=13&categories[]=7' ); - } ); - - it( 'sets the "category_name" filter for categories where the term is a string [DEPRECATED]', () => { - const result = req.category( 'fiction' ); - expect( getQueryStr( result ) ).toBe( 'filter[category_name]=fiction' ); - } ); - - } ); - - describe( 'excludeCategories()', () => { - - beforeEach( () => { - Req.prototype.excludeCategories = parameterMixins.excludeCategories; - } ); - - it( 'mixin is defined', () => { - expect( parameterMixins ).toHaveProperty( 'excludeCategories' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.excludeCategories ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.excludeCategories( 7 ) ).toBe( req ); - } ); - - it( 'sets the "categories_exclude" parameter for a single category ID', () => { - const result = req.excludeCategories( 7 ); - expect( getQueryStr( result ) ).toBe( 'categories_exclude=7' ); - } ); - - it( 'sets the "categories_exclude" parameter for multiple category IDs', () => { - const result = req.excludeCategories( [ 7, 13 ] ); - expect( getQueryStr( result ) ).toBe( 'categories_exclude[]=13&categories_exclude[]=7' ); - } ); - - } ); - - describe( 'tags()', () => { - - beforeEach( () => { - Req.prototype.tags = parameterMixins.tags; - } ); - - it( 'mixin is defined', () => { - expect( parameterMixins ).toHaveProperty( 'tags' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.tags ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.tags( 7 ) ).toBe( req ); - } ); - - it( 'sets the "tags" parameter for a single category ID', () => { - const result = req.tags( 7 ); - expect( getQueryStr( result ) ).toBe( 'tags=7' ); - } ); - - it( 'sets the "tags" parameter for multiple category IDs', () => { - const result = req.tags( [ 7, 13 ] ); - expect( getQueryStr( result ) ).toBe( 'tags[]=13&tags[]=7' ); - } ); - - it( 'sets the "tags" parameter for multiple category IDs provided as numeric strings', () => { - const result = req.tags( [ '7', '13' ] ); - expect( getQueryStr( result ) ).toBe( 'tags[]=13&tags[]=7' ); - } ); - - } ); - - describe( 'tag()', () => { - - beforeEach( () => { - Req.prototype.tag = parameterMixins.tag; - } ); - - it( 'mixin is defined', () => { - expect( parameterMixins ).toHaveProperty( 'tag' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.tag ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.tag( 7 ) ).toBe( req ); - } ); - - it( 'sets the "tag" parameter for a single category ID', () => { - const result = req.tag( 7 ); - expect( getQueryStr( result ) ).toBe( 'tags=7' ); - } ); - - it( 'sets the "tags" parameter for multiple category IDs', () => { - const result = req.tag( [ 7, 13 ] ); - expect( getQueryStr( result ) ).toBe( 'tags[]=13&tags[]=7' ); - } ); - - it( 'sets the "tags" parameter for multiple category IDs provided as numeric strings', () => { - const result = req.tag( [ '7', '13' ] ); - expect( getQueryStr( result ) ).toBe( 'tags[]=13&tags[]=7' ); - } ); - - it( 'sets the "tag" filter when the term is a string [DEPRECATED]', () => { - const result = req.tag( 'bagpipe-techno' ); - expect( getQueryStr( result ) ).toBe( 'filter[tag]=bagpipe-techno' ); - } ); - - } ); - - describe( 'excludeTags()', () => { - - beforeEach( () => { - Req.prototype.excludeTags = parameterMixins.excludeTags; - } ); - - it( 'mixin is defined', () => { - expect( parameterMixins ).toHaveProperty( 'excludeTags' ); - } ); - - it( 'is a function', () => { - expect( typeof parameterMixins.excludeTags ).toBe( 'function' ); - } ); - - it( 'supports chaining', () => { - expect( req.excludeTags( 7 ) ).toBe( req ); - } ); - - it( 'sets the "tags_exclude" parameter for a single category ID', () => { - const result = req.excludeTags( 7 ); - expect( getQueryStr( result ) ).toBe( 'tags_exclude=7' ); - } ); - - it( 'sets the "tags_exclude" parameter for multiple category IDs', () => { - const result = req.excludeTags( [ 7, 13 ] ); - expect( getQueryStr( result ) ).toBe( 'tags_exclude[]=13&tags_exclude[]=7' ); - } ); - - it( 'sets the "tags_exclude" parameter for multiple category IDs provided as numeric strings', () => { - const result = req.excludeTags( [ '7', '13' ] ); - expect( getQueryStr( result ) ).toBe( 'tags_exclude[]=13&tags_exclude[]=7' ); - } ); - - } ); - -} ); diff --git a/tests/unit/lib/route-tree.js b/tests/unit/lib/route-tree.js deleted file mode 100644 index 6d9b062e..00000000 --- a/tests/unit/lib/route-tree.js +++ /dev/null @@ -1,120 +0,0 @@ -'use strict'; - -const routeTree = require( '../../../lib/route-tree' ); -const defaultRoutes = require( '../../../lib/data/default-routes.json' ); - -describe( 'route-tree utility', () => { - - describe( '.build()', () => { - let tree; - - beforeEach( () => { - tree = routeTree.build( defaultRoutes ); - } ); - - it( 'returns an object keyed by API namespace', () => { - const keys = Object.keys( tree ).sort(); - expect( keys.length ).toBe( 2 ); - expect( keys ).toEqual( [ 'oembed/1.0', 'wp/v2' ] ); - } ); - - it( 'includes objects for all default wp/v2 routes', () => { - const routes = Object.keys( tree[ 'wp/v2' ] ).sort(); - expect( routes.length ).toBe( 15 ); - expect( routes.sort() ).toEqual( [ - 'block-renderer', - 'blocks', - 'categories', - 'comments', - 'media', - 'pages', - 'posts', - 'search', - 'settings', - 'statuses', - 'tags', - 'taxonomies', - 'themes', - 'types', - 'users', - ] ); - } ); - - it( 'includes objects for all default oembed/1.0 routes', () => { - const routes = Object.keys( tree[ 'oembed/1.0' ] ).sort(); - expect( routes.length ).toBe( 2 ); - expect( routes.sort().join( ',' ) ).toBe( 'embed,proxy' ); - } ); - - // Inspect the .posts tree as a smoke test for whether parsing the API - // definition object was successful - describe( 'posts resource tree', () => { - let posts; - - beforeEach( () => { - posts = tree[ 'wp/v2' ].posts; - } ); - - it( 'includes a ._getArgs property', () => { - expect( posts ).toHaveProperty( '_getArgs' ); - expect( typeof posts._getArgs ).toBe( 'object' ); - } ); - - it( '._getArgs specifies a list of supported parameters', () => { - expect( posts ).toHaveProperty( '_getArgs' ); - expect( typeof posts._getArgs ).toBe( 'object' ); - expect( posts._getArgs ).toEqual( { - context: {}, - page: {}, - per_page: {}, - search: {}, - after: {}, - author: {}, - author_exclude: {}, - before: {}, - exclude: {}, - id: {}, - include: {}, - offset: {}, - order: {}, - orderby: {}, - parent: {}, - password: {}, - slug: {}, - status: {}, - sticky: {}, - categories: {}, - categories_exclude: {}, - tags: {}, - tags_exclude: {}, - } ); - } ); - - it( 'includes a .posts property', () => { - expect( posts ).toHaveProperty( 'posts' ); - expect( typeof posts.posts ).toBe( 'object' ); - } ); - - // This is a decidedly incomplete smoke test... - // But if this fails, so will everything else! - it( '.posts defines the top level of a route tree', () => { - const routeTree = posts.posts; - expect( routeTree ).toHaveProperty( 'level' ); - expect( routeTree.level ).toBe( 0 ); - expect( routeTree ).toHaveProperty( 'methods' ); - expect( routeTree.methods.sort().join( '|' ) ).toBe( 'get|head|post' ); - expect( routeTree ).toHaveProperty( 'namedGroup' ); - expect( routeTree.namedGroup ).toBe( false ); - expect( routeTree ).toHaveProperty( 'names' ); - expect( routeTree.names ).toEqual( [ 'posts' ] ); - expect( routeTree ).toHaveProperty( 'validate' ); - expect( typeof routeTree.validate ).toBe( 'function' ); - expect( routeTree ).toHaveProperty( 'children' ); - expect( typeof routeTree.children ).toBe( 'object' ); - } ); - - } ); - - } ); - -} ); diff --git a/tests/unit/lib/util/apply-mixin.js b/tests/unit/lib/util/apply-mixin.js deleted file mode 100644 index a42ebfb7..00000000 --- a/tests/unit/lib/util/apply-mixin.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -const applyMixin = require( '../../../../lib/util/apply-mixin' ); - -describe( 'applyMixin utility', () => { - let obj; - - beforeEach( () => { - obj = {}; - } ); - - it( 'is a function', () => { - expect( typeof applyMixin ).toBe( 'function' ); - } ); - - it( 'returns nothing', () => { - expect( applyMixin() ).toBeUndefined(); - } ); - - it( 'assigns a method to the provided object', () => { - const bar = () => {}; - applyMixin( obj, 'foo', bar ); - expect( obj ).toEqual( { - foo: bar, - } ); - } ); - - it( 'does not mutate the object if the specified key exists already', () => { - obj.foo = 'bar'; - applyMixin( obj, 'foo', () => {} ); - expect( obj ).toEqual( { - foo: 'bar', - } ); - } ); - - it( 'does not mutate the object if third arg is not a function', () => { - applyMixin( obj, 'key', 'not a function' ); - expect( obj ).toEqual( {} ); - } ); - -} ); diff --git a/tests/unit/lib/util/argument-is-numeric.js b/tests/unit/lib/util/argument-is-numeric.js deleted file mode 100644 index e9ca358b..00000000 --- a/tests/unit/lib/util/argument-is-numeric.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -const argumentIsNumeric = require( '../../../../lib/util/argument-is-numeric' ); - -describe( 'argumentIsNumeric utility', () => { - - it( 'is a function', () => { - expect( typeof argumentIsNumeric ).toBe( 'function' ); - } ); - - it( 'returns true if provided an integer', () => { - const result = argumentIsNumeric( 7 ); - expect( result ).toBe( true ); - } ); - - it( 'returns true if provided an integer string', () => { - const result = argumentIsNumeric( '2501' ); - expect( result ).toBe( true ); - } ); - - it( 'returns true if provided an array of integers', () => { - const result = argumentIsNumeric( [ 1, 3, 5, 8, 13 ] ); - expect( result ).toBe( true ); - } ); - - it( 'returns true if provided an array of integer strings', () => { - const result = argumentIsNumeric( [ '1', '3', '5', '8', '13' ] ); - expect( result ).toBe( true ); - } ); - - it( 'returns true if provided a mixed array of integers and integer strings', () => { - const result = argumentIsNumeric( [ 1, '3', 5, '8', 13 ] ); - expect( result ).toBe( true ); - } ); - - it( 'returns false if provided a non-integer string', () => { - const result = argumentIsNumeric( 'WordPress' ); - expect( result ).toBe( false ); - } ); - - it( 'returns false if provided a non-integer numeric string value', () => { - const result = argumentIsNumeric( '3.14159' ); - expect( result ).toBe( false ); - } ); - - it( 'returns false if provided an array that contains any non-numeric string', () => { - const result = argumentIsNumeric( [ 1, 3, 5, 'Eight' ] ); - expect( result ).toBe( false ); - } ); - - it( 'returns false if provided an array of strings', () => { - const result = argumentIsNumeric( [ 'One', 'Three' ] ); - expect( result ).toBe( false ); - } ); - - it( 'returns false if provided a non-string, non-integer, non-array value', () => { - expect( argumentIsNumeric( {} ) ).toBe( false ); - expect( argumentIsNumeric( null ) ).toBe( false ); - expect( argumentIsNumeric( /foo/ ) ).toBe( false ); - expect( argumentIsNumeric( false ) ).toBe( false ); - } ); - -} ); diff --git a/tests/unit/lib/util/ensure.js b/tests/unit/lib/util/ensure.js deleted file mode 100644 index 46543233..00000000 --- a/tests/unit/lib/util/ensure.js +++ /dev/null @@ -1,46 +0,0 @@ -'use strict'; - -const ensure = require( '../../../../lib/util/ensure' ); - -describe( 'ensure utility', () => { - let obj; - - beforeEach( () => { - obj = {}; - } ); - - it( 'is defined', () => { - expect( ensure ).toBeDefined(); - } ); - - it( 'is a function', () => { - expect( typeof ensure ).toBe( 'function' ); - } ); - - it( 'sets a default property value on an object', () => { - expect( obj ).not.toHaveProperty( 'foo' ); - ensure( obj, 'foo', 'bar' ); - expect( obj ).toHaveProperty( 'foo' ); - expect( typeof obj.foo ).toBe( 'string' ); - expect( obj.foo ).toBe( 'bar' ); - } ); - - it( 'will not overwrite an existing value on an object', () => { - obj.foo = 'baz'; - expect( obj ).toHaveProperty( 'foo' ); - ensure( obj, 'foo', 'bar' ); - expect( obj ).toHaveProperty( 'foo' ); - expect( typeof obj.foo ).toBe( 'string' ); - expect( obj.foo ).toBe( 'baz' ); - } ); - - it( 'will not overwrite a falsy value on an object', () => { - obj.foo = 0; - expect( obj ).toHaveProperty( 'foo' ); - ensure( obj, 'foo', 'bar' ); - expect( obj ).toHaveProperty( 'foo' ); - expect( typeof obj.foo ).toBe( 'number' ); - expect( obj.foo ).toBe( 0 ); - } ); - -} ); diff --git a/tests/unit/lib/util/is-empty-object.js b/tests/unit/lib/util/is-empty-object.js deleted file mode 100644 index 2646f2cf..00000000 --- a/tests/unit/lib/util/is-empty-object.js +++ /dev/null @@ -1,54 +0,0 @@ -'use strict'; - -const isEmptyObject = require( '../../../../lib/util/is-empty-object' ); - -describe( 'isEmptyObject utility', () => { - - it( 'is defined', () => { - expect( isEmptyObject ).toBeDefined(); - } ); - - it( 'is a function', () => { - expect( typeof isEmptyObject ).toBe( 'function' ); - } ); - - it( 'returns true if passed an empty object', () => { - expect( isEmptyObject( {} ) ).toBe( true ); - } ); - - it( 'returns true if passed a constructed object with no instance properties', () => { - function Ctor() {} - Ctor.prototype.prop = 'val'; - expect( isEmptyObject( new Ctor() ) ).toBe( true ); - } ); - - it( 'returns false if passed an object with own properties', () => { - expect( isEmptyObject( { prop: 'value' } ) ).toBe( false ); - } ); - - it( 'returns false if passed a constructed object with instance properties', () => { - function Ctor() { - this.prop = 'val'; - } - expect( isEmptyObject( new Ctor() ) ).toBe( false ); - } ); - - it( 'returns false if passed a string', () => { - expect( isEmptyObject( '{}' ) ).toBe( false ); - } ); - - it( 'returns false if passed an empty array', () => { - expect( isEmptyObject( [] ) ).toBe( false ); - } ); - - it( 'returns false if passed a boolean', () => { - expect( isEmptyObject( true ) ).toBe( false ); - expect( isEmptyObject( false ) ).toBe( false ); - } ); - - it( 'returns false if passed a number', () => { - expect( isEmptyObject( 0 ) ).toBe( false ); - expect( isEmptyObject( 1337 ) ).toBe( false ); - } ); - -} ); diff --git a/tests/unit/lib/util/key-val-to-obj.js b/tests/unit/lib/util/key-val-to-obj.js deleted file mode 100644 index c2684550..00000000 --- a/tests/unit/lib/util/key-val-to-obj.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -const keyValToObj = require( '../../../../lib/util/key-val-to-obj' ); - -describe( 'keyValToObj utility', () => { - - it( 'is defined', () => { - expect( keyValToObj ).toBeDefined(); - } ); - - it( 'is a function', () => { - expect( typeof keyValToObj ).toBe( 'function' ); - } ); - - it( 'returns an object', () => { - expect( typeof keyValToObj() ).toBe( 'object' ); - } ); - - it( 'sets the specified value at the provided key on the returned object', () => { - const result = keyValToObj( 'propName', 123456 ); - expect( result ).toHaveProperty( 'propName' ); - expect( result ).toEqual( { - propName: 123456, - } ); - } ); - - it( 'can be used to set an array, and sets values by reference', () => { - const arr = [ 'mimsy', 'borogoves', 'outgrabe' ]; - const result = keyValToObj( 'words', arr ); - expect( result ).toHaveProperty( 'words' ); - expect( result.words ).toBe( arr ); - expect( result ).toEqual( { - words: [ 'mimsy', 'borogoves', 'outgrabe' ], - } ); - } ); - -} ); diff --git a/tests/unit/lib/util/named-group-regexp.js b/tests/unit/lib/util/named-group-regexp.js deleted file mode 100644 index 66a2ed5a..00000000 --- a/tests/unit/lib/util/named-group-regexp.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -const namedGroupRE = require( '../../../../lib/util/named-group-regexp' ).namedGroupRE; - -describe( 'named PCRE group RegExp', () => { - - it( 'is a regular expression', () => { - expect( namedGroupRE ).toBeInstanceOf( RegExp ); - } ); - - it( 'will not match an arbitrary string', () => { - const pathComponent = 'author'; - const result = pathComponent.match( namedGroupRE ); - expect( result ).toBeNull(); - } ); - - it( 'identifies the name and RE pattern for a PCRE named group', () => { - const pathComponent = '(?P[\\d]+)'; - const result = pathComponent.match( namedGroupRE ); - expect( result ).not.toBeNull(); - expect( result[ 1 ] ).toBe( 'parent' ); - expect( result[ 2 ] ).toBe( '[\\d]+' ); - } ); - - it( 'identifies the name and RE pattern for another group', () => { - const pathComponent = '(?P\\d+)'; - const result = pathComponent.match( namedGroupRE ); - expect( result ).not.toBeNull(); - expect( result[ 1 ] ).toBe( 'id' ); - expect( result[ 2 ] ).toBe( '\\d+' ); - } ); - - it( 'identifies RE patterns including forward slashes', () => { - const pathComponent = '(?P[a-z\\/\\.\\-_]+)'; - const result = pathComponent.match( namedGroupRE ); - expect( result ).not.toBeNull(); - expect( result[ 1 ] ).toBe( 'plugin' ); - expect( result[ 2 ] ).toBe( '[a-z\\/\\.\\-_]+' ); - } ); - - it( 'will match an empty string if a "RE Pattern" if the pattern is omitted', () => { - const pathComponent = '(?P)'; - const result = pathComponent.match( namedGroupRE ); - expect( result ).not.toBeNull(); - expect( result[ 1 ] ).toBe( 'id' ); - expect( result[ 2 ] ).toBe( '' ); - } ); - -} ); diff --git a/tests/unit/lib/util/object-reduce.js b/tests/unit/lib/util/object-reduce.js deleted file mode 100644 index 0cde240a..00000000 --- a/tests/unit/lib/util/object-reduce.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -const objectReduce = require( '../../../../lib/util/object-reduce' ); - -describe( 'objectReduce utility', () => { - - it( 'is defined', () => { - expect( objectReduce ).toBeDefined(); - } ); - - it( 'is a function', () => { - expect( typeof objectReduce ).toBe( 'function' ); - } ); - - it( 'resolves to the provided initial value if called on an empty object', () => { - expect( objectReduce( {}, () => {}, 'Sasquatch' ) ).toBe( 'Sasquatch' ); - } ); - - it( 'can be used to reduce over an object', () => { - const result = objectReduce( { - key1: 'val1', - key2: 'val2', - key3: 'val3', - }, ( memo, val, key ) => memo + val + key, 'result:' ); - expect( result ).toBe( 'result:val1key1val2key2val3key3' ); - } ); - -} ); diff --git a/tests/unit/lib/util/parameter-setter.js b/tests/unit/lib/util/parameter-setter.js deleted file mode 100644 index a5fb93e5..00000000 --- a/tests/unit/lib/util/parameter-setter.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -const paramSetter = require( '../../../../lib/util/parameter-setter' ); - -describe( 'parameterSetter utility', () => { - let obj; - - beforeEach( () => { - obj = {}; - } ); - - it( 'is a function', () => { - expect( typeof paramSetter ).toBe( 'function' ); - } ); - - it( 'returns a function', () => { - expect( typeof paramSetter() ).toBe( 'function' ); - } ); - - it( 'creates a setter that calls this.param()', () => { - obj.param = jest.fn(); - obj.setter = paramSetter( 'foo' ); - obj.setter( 'bar' ); - expect( obj.param ).toHaveBeenCalledWith( 'foo', 'bar' ); - } ); - -} ); diff --git a/tests/unit/lib/util/split-path.js b/tests/unit/lib/util/split-path.js deleted file mode 100644 index 982dcec3..00000000 --- a/tests/unit/lib/util/split-path.js +++ /dev/null @@ -1,57 +0,0 @@ -'use strict'; - -const splitPath = require( '../../../../lib/util/split-path' ); - -describe( 'splitPath utility', () => { - - it( 'is a function', () => { - expect( typeof splitPath ).toBe( 'function' ); - } ); - - it( 'splits a simple path on the "/" character', () => { - expect( splitPath( 'a/b/c/d' ) ).toEqual( [ 'a', 'b', 'c', 'd' ] ); - } ); - - it( 'correctly splits a string containing named capture groups', () => { - const result = splitPath( '/posts/(?P[\\d]+)/revisions/(?P[\\d]+)' ); - expect( result ).toEqual( [ - 'posts', - '(?P[\\d]+)', - 'revisions', - '(?P[\\d]+)', - ] ); - } ); - - it( 'correctly splits a string when a named group regex contains a "/"', () => { - const result = splitPath( '/plugin/(?P[a-z\\/\\.\\-_]+)' ); - expect( result ).toEqual( [ - 'plugin', - '(?P[a-z\\/\\.\\-_]+)', - ] ); - } ); - - it( 'correctly splits a string with levels containing text outside named groups', () => { - // From user-contributed example on https://developer.wordpress.org/reference/functions/register_rest_route/ - // Note that this library does not support this syntax, but ensuring that - // common variants of path strings are split correctly avoids situations - // where an unexpected string format could cause an error. - const result = splitPath( '/users/market=(?P[a-zA-Z0-9-]+)/lat=(?P[a-z0-9 .\\-]+)/long=(?P[a-z0-9 .\\-]+)' ); - expect( result ).toEqual( [ - 'users', - 'market=(?P[a-zA-Z0-9-]+)', - 'lat=(?P[a-z0-9 .\\-]+)', - 'long=(?P[a-z0-9 .\\-]+)', - ] ); - } ); - - it( 'correctly splits a string with this situation', () => { - const result = splitPath( '/plugin/(?P[^/]+)/committers/?' ); - expect( result ).toEqual( [ - 'plugin', - '(?P[^/]+)', - 'committers', - '?', - ] ); - } ); - -} ); diff --git a/tests/unit/lib/util/unique.js b/tests/unit/lib/util/unique.js deleted file mode 100644 index e568396a..00000000 --- a/tests/unit/lib/util/unique.js +++ /dev/null @@ -1,27 +0,0 @@ -const unique = require( '../../../../lib/util/unique' ); - -describe( 'unique', () => { - - it( 'is a function', () => { - expect( typeof unique ).toBe( 'function' ); - } ); - - it( 'returns an array unchanged if there are no repeated items', () => { - expect( unique( [ 1, 2, 3, 4 ] ) ).toEqual( [ 1, 2, 3, 4 ] ); - expect( unique( [ 'a', 'b', 'c' ] ) ).toEqual( [ 'a', 'b', 'c' ] ); - const obj1 = {}; - const obj2 = {}; - const obj3 = {}; - expect( unique( [ obj1, obj2, obj3 ] ) ).toEqual( [ obj1, obj2, obj3 ] ); - } ); - - it( 'returns an array with duplicated items removed', () => { - expect( unique( [ 1, 2, 3, 4, 1, 2 ] ) ).toEqual( [ 1, 2, 3, 4 ] ); - expect( unique( [ 'a', 'b', 'c', 'a', 'b' ] ) ).toEqual( [ 'a', 'b', 'c' ] ); - const obj1 = {}; - const obj2 = {}; - const obj3 = {}; - expect( unique( [ obj1, obj2, obj1, obj3, obj2 ] ) ).toEqual( [ obj1, obj2, obj3 ] ); - } ); - -} ); diff --git a/tests/unit/lib/wp-register-route.js b/tests/unit/lib/wp-register-route.js deleted file mode 100644 index 2211ed75..00000000 --- a/tests/unit/lib/wp-register-route.js +++ /dev/null @@ -1,577 +0,0 @@ -'use strict'; - -const WPRequest = require( '../../../lib/constructors/wp-request' ); -const registerRoute = require( '../../../lib/wp-register-route' ); -const checkMethodSupport = require( '../../../lib/util/check-method-support' ); -const mixins = require( '../../../lib/mixins' ); - -describe( 'wp.registerRoute', () => { - - it( 'is a function', () => { - expect( typeof registerRoute ).toBe( 'function' ); - } ); - - it( 'returns a function', () => { - expect( typeof registerRoute( 'a', 'b' ) ).toBe( 'function' ); - } ); - - it( 'sets a Ctor property on the returned function', () => { - const result = registerRoute( 'a', 'b' ); - expect( result ).toHaveProperty( 'Ctor' ); - } ); - - it( 'returns a factory that returns Ctor instances', () => { - const result = registerRoute( 'a', 'b' ); - expect( result() ).toBeInstanceOf( result.Ctor ); - } ); - - it( 'returns a factory for an object which extends WPRequest', () => { - const result = registerRoute( 'a', 'b' ); - expect( result() ).toBeInstanceOf( WPRequest ); - } ); - - it( 'factory-generated handlers have all the expected WPRequest methods', () => { - const factory = registerRoute( 'a', 'b' ); - const handler = factory( { - endpoint: '/', - } ); - // spot check - expect( typeof handler.page ).toBe( 'function' ); - expect( typeof handler.perPage ).toBe( 'function' ); - expect( typeof handler.offset ).toBe( 'function' ); - expect( typeof handler.context ).toBe( 'function' ); - expect( typeof handler.include ).toBe( 'function' ); - expect( typeof handler.slug ).toBe( 'function' ); - const result = handler.page( 7 ).perPage( 2 ).exclude( [ 42, 7 ] ).toString(); - expect( result ).toBe( '/a/b?exclude%5B%5D=42&exclude%5B%5D=7&page=7&per_page=2' ); - } ); - - // custom route example for wp-api.org - describe( 'handler for /author/(?P\\d+)', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'myplugin/v1', '/author/(?P\\d+)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'renders a route prefixed with the provided namespace', () => { - expect( handler.toString() ).toMatch( /myplugin\/v1/ ); - } ); - - it( 'sets the /authors/ path part automatically', () => { - expect( handler.toString() ).toBe( '/myplugin/v1/author' ); - } ); - - describe( '.id() method', () => { - - it( 'is defined', () => { - expect( handler ).toHaveProperty( 'id' ); - } ); - - it( 'is a function', () => { - expect( typeof handler.id ).toBe( 'function' ); - } ); - - it( 'sets the ID component of the path', () => { - expect( handler.id( 3263827 ).toString() ).toBe( '/myplugin/v1/author/3263827' ); - } ); - - } ); - - } ); - - // Example of a Jetpack route with regexes containing forward slashes - describe( 'handler for /jetpack/v4/plugin/(?P[a-z\\/\\.\\-_]+)', () => { - - it( 'permits setting path parts with forward slashes', () => { - const factory = registerRoute( 'jetpack/v4', '/plugin/(?P[a-z\\/\\.\\-_]+)' ); - const handler = factory( { - endpoint: '/', - } ); - expect( handler ).toHaveProperty( 'plugin' ); - expect( typeof handler.plugin ).toBe( 'function' ); - expect( handler.plugin( 'a/b_c' ).toString() ).toBe( '/jetpack/v4/plugin/a/b_c' ); - } ); - - } ); - - describe( 'handler for /plugin/(?P[^/]+)/committers/?)', () => { - - it( 'will ignore the trailing /? (the ? is intended to mark the / as optional', () => { - const factory = registerRoute( 'plugins/v1', '/plugin/(?P[^/]+)/committers/?' ); - const handler = factory( { - endpoint: '/', - } ); - expect( handler ).toHaveProperty( 'pluginSlug' ); - expect( typeof handler.pluginSlug ).toBe( 'function' ); - expect( handler ).toHaveProperty( 'committers' ); - expect( typeof handler.committers ).toBe( 'function' ); - expect( handler.pluginSlug( 'rest-api' ).committers().toString() ).toBe( '/plugins/v1/plugin/rest-api/committers' ); - } ); - - } ); - - describe( 'handler for unsupported route definition format', () => { - - it( 'will parse the route without error but not yield functioning setters', () => { - let factory; - expect( () => { - factory = registerRoute( - 'mmw/v1', - '/users/market=(?P[a-zA-Z0-9-]+)/lat=(?P[a-z0-9 .\\-]+)/long=(?P[a-z0-9 .\\-]+)' - ); - } ).not.toThrow(); - const handler = factory( { - endpoint: '/', - } ); - expect( handler ).toHaveProperty( 'market' ); - expect( typeof handler.market ).toBe( 'function' ); - expect( handler ).toHaveProperty( 'lat' ); - expect( typeof handler.lat ).toBe( 'function' ); - expect( handler ).toHaveProperty( 'long' ); - expect( typeof handler.long ).toBe( 'function' ); - // This is not "correct", but this syntax is not supported: the purpose of this - // test is to ensure that the code executes without error - expect( handler.market( 'nz' ).lat( '40.9006 S' ).long( '174.8860 E' ).toString() ) - .toBe( '/mmw/v1/users/nz/40.9006 S/174.8860 E' ); - } ); - - } ); - - describe( 'handler for /a/(?P\\d+)', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'ns', '/a/(?P\\d+)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'camelCases the setter name', () => { - expect( handler ).not.toHaveProperty( 'snake_cased_path_setter' ); - expect( handler ).toHaveProperty( 'snakeCasedPathSetter' ); - expect( typeof handler.snakeCasedPathSetter ).toBe( 'function' ); - } ); - - } ); - - describe( 'handler for /a/(?P\\d+)', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'ns', '/a/(?P\\d+)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'camelCases the setter name', () => { - expect( handler ).not.toHaveProperty( 'kebab-cased-path-setter' ); - expect( handler ).toHaveProperty( 'kebabCasedPathSetter' ); - expect( typeof handler.kebabCasedPathSetter ).toBe( 'function' ); - } ); - - } ); - - describe( 'handler for /a/(?P\\d+)', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'ns', '/a/(?P\\d+)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'does not mutate the setter name', () => { - expect( handler ).not.toHaveProperty( 'camelcasedpathsetter' ); - expect( handler ).toHaveProperty( 'camelCasedPathSetter' ); - expect( typeof handler.camelCasedPathSetter ).toBe( 'function' ); - } ); - - } ); - - describe( 'handler for route with capture group named identically to existing method', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'ns', '/route/(?P)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'does not overwrite preexisting methods', () => { - expect( handler.param ).toBe( WPRequest.prototype.param ); - expect( handler.param( 'foo', 'bar' ).toString() ).toBe( '/ns/route?foo=bar' ); - expect( handler.param( 'foo', 'bar' ).toString() ).not.toBe( '/ns/route/foo' ); - } ); - - } ); - - describe( 'handler for consecutive dynamic route segments', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'ns', '/resource/(?P\\d+)/(?P\\d+)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - describe( 'part1 method', () => { - - it( 'is defined', () => { - expect( handler ).toHaveProperty( 'part1' ); - } ); - - it( 'is a function', () => { - expect( typeof handler.part1 ).toBe( 'function' ); - } ); - - it( 'sets the part1 component of the path', () => { - expect( handler.part1( 12 ).toString() ).toBe( '/ns/resource/12' ); - } ); - - } ); - - describe( 'part2 method', () => { - - it( 'is defined', () => { - expect( handler ).toHaveProperty( 'part2' ); - } ); - - it( 'is a function', () => { - expect( typeof handler.part2 ).toBe( 'function' ); - } ); - - it( 'sets the part2 component of the path', () => { - expect( handler.part1( 12 ).part2( 34 ).toString() ).toBe( '/ns/resource/12/34' ); - } ); - - } ); - - } ); - - describe( 'parameters', () => { - let handler; - - it( 'assign any mixins that match provided parameter names', () => { - const factory = registerRoute( 'a', '/b', { - params: [ 'filter', 'author' ], - } ); - handler = factory( { - endpoint: '/', - } ); - expect( handler ).toHaveProperty( 'filter' ); - expect( handler.filter ).toBe( mixins.filter.filter ); - expect( handler ).toHaveProperty( 'author' ); - expect( handler.author ).toBe( mixins.author.author ); - } ); - - it( 'does nothing if non-string parameters are provided', () => { - const factory1 = registerRoute( 'a', 'b' ); - const factory2 = registerRoute( 'a', 'b', { - params: [ null, () => {} ], - } ); - expect( factory1 ).not.toBe( factory2 ); - expect( factory1.Ctor ).not.toBe( factory2.Ctor ); - const getPrototypeMethods = ( factoryFn ) => { - const proto = factoryFn.Ctor.prototype; - return Object.keys( proto ).filter( key => typeof proto[ key ] === 'function' ); - }; - const factory1PrototypeMethods = getPrototypeMethods( factory1 ); - const factory2PrototypeMethods = getPrototypeMethods( factory2 ); - expect( factory1PrototypeMethods ).toEqual( factory2PrototypeMethods ); - } ); - - it( 'creates a .param() wrapper for params that do not match existing mixins', () => { - const factory = registerRoute( 'a', 'b', { - params: [ 'customtax', 'someparam' ], - } ); - handler = factory( { - endpoint: '/', - } ); - expect( handler ).toHaveProperty( 'customtax' ); - expect( typeof handler.customtax ).toBe( 'function' ); - expect( handler ).toHaveProperty( 'someparam' ); - expect( typeof handler.someparam ).toBe( 'function' ); - const result = handler.customtax( 'techno' ).someparam( [ - 'tech', - 'yes', - ] ); - expect( result.toString() ).toBe( '/a/b?customtax=techno&someparam%5B%5D=tech&someparam%5B%5D=yes' ); - } ); - - it( 'will not overwrite existing methods', () => { - const factory = registerRoute( 'myplugin/v1', '/author/(?P\\d+)', { - params: [ 'param', 'edit', 'id' ], - } ); - handler = factory( { - endpoint: '/', - } ); - const result = handler.id( 7 ).param( 'a', 'b' ).edit().toString(); - expect( result ).toBe( '/myplugin/v1/author/7?a=b&context=edit' ); - } ); - - } ); - - describe( 'mixins', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'myplugin/v1', '/author/(?P\\d+)', { - mixins: { - foo() { - return this.param( 'foo', true ); - }, - bar( val ) { - return this.param( 'bar', val ); - }, - }, - } ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'are set on the prototype of the handler constructor', () => { - expect( handler ).toHaveProperty( 'foo' ); - expect( handler.hasOwnProperty( 'foo' ) ).toBe( false ); - expect( typeof handler.foo ).toBe( 'function' ); - expect( handler ).toHaveProperty( 'bar' ); - expect( handler.hasOwnProperty( 'bar' ) ).toBe( false ); - expect( typeof handler.bar ).toBe( 'function' ); - } ); - - it( 'can set URL query parameters', () => { - expect( handler.foo().toString() ).toBe( '/myplugin/v1/author?foo=true' ); - } ); - - it( 'can set dynamic URL query parameter values', () => { - expect( handler.bar( '1138' ).toString() ).toBe( '/myplugin/v1/author?bar=1138' ); - } ); - - it( 'will not overwrite existing endpoint handler prototype methods', () => { - const factory = registerRoute( 'myplugin/v1', '/author/(?P\\d+)', { - mixins: { - id() { - return this.param( 'id', 'as_a_param' ); - }, - }, - } ); - const result = factory( { - endpoint: '/', - } ).id( 7 ).toString(); - expect( result ).not.toBe( '/myplugin/v1/author?id=as_a_param' ); - expect( result ).toBe( '/myplugin/v1/author/7' ); - } ); - - it( 'will not overwrite WPRequest default methods', () => { - const factory = registerRoute( 'myplugin/v1', '/author/(?P\\d+)', { - mixins: { - param() { - throw new Error(); - }, - }, - } ); - const result = factory( { - endpoint: '/', - } ).id( 7 ).param( 'a', 'b' ).toString(); - expect( result ).toBe( '/myplugin/v1/author/7?a=b' ); - } ); - - } ); - - describe( 'handler for multi-capture group route', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'wp/v2', 'pages/(?P[\\d]+)/revisions/(?P[\\d]+)' ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'sets the first static level of the route automatically', () => { - expect( handler.toString() ).toBe( '/wp/v2/pages' ); - } ); - - it( 'permits the first dynamic level of the route to be set with .parent', () => { - expect( handler.parent( 79 ).toString() ).toBe( '/wp/v2/pages/79' ); - } ); - - it( 'permits the second static level of the route to be set with .revisions', () => { - expect( handler.parent( 79 ).revisions().toString() ).toBe( '/wp/v2/pages/79/revisions' ); - } ); - - it( 'permits the second dynamic level of the route to be set with .id', () => { - expect( handler.parent( 79 ).revisions().id( 97 ).toString() ).toBe( '/wp/v2/pages/79/revisions/97' ); - } ); - - it( 'throws an error if the parts of the route provided are not contiguous', () => { - expect( () => { - handler.parent( 101 ).id( 102 ).toString(); - } ).toThrow(); - } ); - - } ); - - describe( 'handler validation', () => { - let handler; - - it( 'can be enforced by providing a regex for a capture group', () => { - const factory = registerRoute( 'myplugin', 'one/(?P\\w+_\\d+)' ); - handler = factory( { - endpoint: '/', - } ); - expect( () => { - handler.a( 'foo' ).toString(); - } ).toThrow; - expect( handler.a( 'foo_100' ).toString() ).toBe( '/myplugin/one/foo_100' ); - } ); - - it( 'can be bypassed if no regex is provided for a capture group', () => { - const factory = registerRoute( 'myplugin', 'one/(?P)/two/(?P)' ); - handler = factory( { - endpoint: '/', - } ); - expect( () => { - handler.a( 'foo' ).two().b( 1000 ).toString(); - } ).not.toThrow; - expect( handler.a( 'foo' ).two( 1000 ).toString() ).toBe( '/myplugin/one/foo/two/1000' ); - } ); - - } ); - - describe( 'method option:', () => { - let handler; - - beforeEach( () => { - const factory = registerRoute( 'myplugin', 'one/(?P)/(?P)', { - methods: [ 'GET', 'POST' ], - } ); - handler = factory( { - endpoint: '/', - } ); - } ); - - describe( 'leaf nodes', () => { - - describe( 'support whitelisted method', () => { - - [ 'get', 'post' ].forEach( ( method ) => { - it( method, () => { - expect( () => { - checkMethodSupport( method, handler.a( 1 ).b( 2 ) ); - } ).not.toThrow(); - } ); - } ); - - } ); - - describe( 'blacklist method', () => { - - [ 'delete', 'put' ].forEach( ( method ) => { - it( method, () => { - expect( () => { - checkMethodSupport( method, handler.a( 1 ).b( 2 ) ); - } ).toThrow(); - } ); - } ); - - } ); - - it( 'support "head" implicitly if "get" is whitelisted', () => { - expect( () => { - checkMethodSupport( 'head', handler.a( 1 ).b( 2 ) ); - } ).not.toThrow(); - } ); - - it( 'support "get" implicitly if "head" is whitelisted', () => { - const factory = registerRoute( 'myplugin', 'one/(?P)/(?P)', { - methods: [ 'HEAD' ], - } ); - handler = factory( { - endpoint: '/', - } ); - expect( () => { - checkMethodSupport( 'head', handler.a( 1 ).b( 2 ) ); - } ).not.toThrow(); - } ); - - } ); - - describe( 'non-leaf nodes', () => { - - describe( 'support all methods', () => { - - [ 'get', 'post', 'head', 'put', 'delete' ].forEach( ( method ) => { - it( method, () => { - expect( () => { - checkMethodSupport( method, handler.a( 1 ) ); - } ).not.toThrow(); - } ); - } ); - - } ); - - } ); - - describe( 'specified as a string', () => { - - beforeEach( () => { - const factory = registerRoute( 'myplugin', 'one/(?P)/(?P)', { - methods: 'POST', - } ); - handler = factory( { - endpoint: '/', - } ); - } ); - - it( 'is properly whitelisted', () => { - expect( () => { - checkMethodSupport( 'post', handler.a( 1 ).b( 2 ) ); - } ).not.toThrow(); - } ); - - describe( 'implicitly blacklists other method', () => { - - [ 'get', 'head', 'delete', 'put' ].forEach( ( method ) => { - it( method, () => { - expect( () => { - checkMethodSupport( method, handler.a( 1 ).b( 2 ) ); - } ).toThrow(); - } ); - } ); - - } ); - - } ); - - } ); - - describe( 'handler options', () => { - - it( 'can be passed in to the factory method', () => { - const factory = registerRoute( 'myplugin', 'myroute' ); - expect( factory( { endpoint: '/wp-yaml/' } ).toString() ).toBe( '/wp-yaml/myplugin/myroute' ); - } ); - - it( 'correctly defaults to the containing object\'s _options, if present', () => { - const obj = { - factory: registerRoute( 'myplugin', 'myroute' ), - _options: { - endpoint: '/foo/', - }, - }; - expect( obj.factory().toString() ).toBe( '/foo/myplugin/myroute' ); - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/comments.js b/tests/unit/route-handlers/comments.js deleted file mode 100644 index 2fb36112..00000000 --- a/tests/unit/route-handlers/comments.js +++ /dev/null @@ -1,127 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.comments', () => { - let site; - let comments; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - comments = site.comments(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - comments = site.comments( { - endpoint: '/custom-endpoint/', - } ); - expect( comments._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( comments._options.endpoint ).toBe( '/wp-json/' ); - expect( comments._options.username ).toBe( 'foouser' ); - expect( comments._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( comments.toString() ).toBe( '/wp-json/wp/v2/comments' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( comments ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( comments._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit CommentsRequest from WPRequest', () => { - expect( comments instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( 'path part setters', () => { - - describe( '.id()', () => { - - it( 'provides a method to set the ID', () => { - expect( comments ).toHaveProperty( 'id' ); - expect( typeof comments.id ).toBe( 'function' ); - } ); - - it( 'should set the ID value in the path', () => { - comments.id( 314159 ); - expect( comments.toString() ).toBe( '/wp-json/wp/v2/comments/314159' ); - } ); - - it( 'accepts ID parameters as strings', () => { - comments.id( '8' ); - expect( comments.toString() ).toBe( '/wp-json/wp/v2/comments/8' ); - } ); - - it( 'should update the supported methods when setting ID', () => { - comments.id( 8 ); - const _supportedMethods = comments._supportedMethods.sort().join( '|' ); - expect( _supportedMethods ).toBe( 'delete|get|head|patch|post|put' ); - } ); - - } ); - - } ); - - describe( 'URL Generation', () => { - - it( 'should create the URL for retrieving all comments', () => { - const path = comments.toString(); - expect( path ).toBe( '/wp-json/wp/v2/comments' ); - } ); - - it( 'should create the URL for retrieving a specific comment', () => { - const path = comments.id( 1337 ).toString(); - expect( path ).toBe( '/wp-json/wp/v2/comments/1337' ); - } ); - - it( 'does not throw an error if a valid numeric ID is specified', () => { - expect( () => { - comments.id( 8 ); - comments.validatePath(); - } ).not.toThrow(); - } ); - - it( 'does not throw an error if a valid numeric ID is specified as a string', () => { - expect( () => { - comments.id( '8' ); - comments.validatePath(); - } ).not.toThrow(); - } ); - - it( 'throws an error if a non-integer numeric string ID is specified', () => { - expect( () => { - comments.id( 4.019 ); - comments.validatePath(); - } ).toThrow(); - } ); - - it( 'throws an error if a non-numeric string ID is specified', () => { - expect( () => { - comments.id( 'wombat' ); - comments.validatePath(); - } ).toThrow(); - } ); - - it( 'should restrict path changes to a single instance', () => { - comments.id( 2 ); - const newComments = site.comments().id( 3 ); - expect( comments.toString() ).toBe( '/wp-json/wp/v2/comments/2' ); - expect( newComments.toString() ).toBe( '/wp-json/wp/v2/comments/3' ); - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/media.js b/tests/unit/route-handlers/media.js deleted file mode 100644 index b380ae28..00000000 --- a/tests/unit/route-handlers/media.js +++ /dev/null @@ -1,115 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.media', () => { - let site; - let media; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - media = site.media(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - media = site.media( { - endpoint: '/custom-endpoint/', - } ); - expect( media._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( media._options.endpoint ).toBe( '/wp-json/' ); - expect( media._options.username ).toBe( 'foouser' ); - expect( media._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( media.toString() ).toBe( '/wp-json/wp/v2/media' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( media ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( media._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit MediaRequest from WPRequest', () => { - expect( media instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( '.id()', () => { - - it( 'is defined', () => { - expect( media ).toHaveProperty( 'id' ); - } ); - - it( 'is a function', () => { - expect( typeof media.id ).toBe( 'function' ); - } ); - - it( 'should set the ID value in the path', () => { - media.id( 8 ); - expect( media.toString() ).toBe( '/wp-json/wp/v2/media/8' ); - } ); - - it( 'should update the supported methods', () => { - media.id( 8 ); - const _supportedMethods = media._supportedMethods.sort().join( '|' ); - expect( _supportedMethods ).toBe( 'delete|get|head|patch|post|put' ); - } ); - - it( 'throws an error on successive calls', () => { - expect( () => { - media.id( 8 ).id( 3 ); - } ).toThrow(); - } ); - - it( 'passes validation when called with a number', () => { - expect( () => { - media.id( 8 )._renderPath(); - } ).not.toThrow(); - } ); - - it( 'passes validation when called with a number formatted as a string', () => { - expect( () => { - media.id( '9' )._renderPath(); - } ).not.toThrow(); - } ); - - it( 'causes a validation error when called with a non-number', () => { - expect( () => { - media.id( 'wombat' )._renderPath(); - } ).toThrow(); - } ); - - } ); - - describe( 'url generation', () => { - - it( 'should create the URL for the media collection', () => { - const uri = media.toString(); - expect( uri ).toBe( '/wp-json/wp/v2/media' ); - } ); - - it( 'can paginate the media collection responses', () => { - const uri = media.page( 4 ).toString(); - expect( uri ).toBe( '/wp-json/wp/v2/media?page=4' ); - } ); - - it( 'should create the URL for a specific media object', () => { - const uri = media.id( 1492 ).toString(); - expect( uri ).toBe( '/wp-json/wp/v2/media/1492' ); - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/pages.js b/tests/unit/route-handlers/pages.js deleted file mode 100644 index ade5dbf6..00000000 --- a/tests/unit/route-handlers/pages.js +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.pages', () => { - let site; - let pages; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - pages = site.pages(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - pages = site.pages( { - endpoint: '/custom-endpoint/', - } ); - expect( pages._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( pages._options.endpoint ).toBe( '/wp-json/' ); - expect( pages._options.username ).toBe( 'foouser' ); - expect( pages._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( pages.toString() ).toBe( '/wp-json/wp/v2/pages' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( pages ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( pages._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit PagesRequest from WPRequest', () => { - expect( pages instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( 'URL Generation', () => { - - it( 'should restrict path changes to a single instance', () => { - pages.id( 2 ); - const newPages = site.pages().id( 3 ).revisions(); - expect( pages.toString() ).toBe( '/wp-json/wp/v2/pages/2' ); - expect( newPages.toString() ).toBe( '/wp-json/wp/v2/pages/3/revisions' ); - } ); - - describe( 'page collections', () => { - - it( 'should create the URL for retrieving all pages', () => { - expect( pages.toString() ).toBe( '/wp-json/wp/v2/pages' ); - } ); - - it( 'should provide filtering methods', () => { - expect( typeof pages.slug ).toBe( 'function' ); - const path = pages.slug( 'some-slug' ).toString(); - expect( path ).toBe( '/wp-json/wp/v2/pages?slug=some-slug' ); - } ); - - } ); - - describe( '.id()', () => { - - it( 'is defined', () => { - expect( pages ).toHaveProperty( 'id' ); - expect( typeof pages.id ).toBe( 'function' ); - } ); - - it( 'should create the URL for retrieving a specific post', () => { - const path = pages.id( 1337 ).toString(); - expect( path ).toBe( '/wp-json/wp/v2/pages/1337' ); - } ); - - it( 'should update the supported methods when setting ID', () => { - pages.id( 8 ); - const _supportedMethods = pages._supportedMethods.sort().join( '|' ); - expect( _supportedMethods ).toBe( 'delete|get|head|patch|post|put' ); - } ); - - } ); - - describe( '.revisions()', () => { - - it( 'is defined', () => { - expect( pages ).toHaveProperty( 'revisions' ); - } ); - - it( 'is a function', () => { - expect( typeof pages.revisions ).toBe( 'function' ); - } ); - - it( 'should create the URL for retrieving the revisions for a specific post', () => { - const path = pages.id( 1337 ).revisions().toString(); - expect( path ).toBe( '/wp-json/wp/v2/pages/1337/revisions' ); - } ); - - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/posts.js b/tests/unit/route-handlers/posts.js deleted file mode 100644 index 53086546..00000000 --- a/tests/unit/route-handlers/posts.js +++ /dev/null @@ -1,154 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.posts', () => { - let site; - let posts; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - posts = site.posts(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - posts = site.posts( { - endpoint: '/custom-endpoint/', - } ); - expect( posts._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( posts._options.endpoint ).toBe( '/wp-json/' ); - expect( posts._options.username ).toBe( 'foouser' ); - expect( posts._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( posts.toString() ).toBe( '/wp-json/wp/v2/posts' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( posts ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( posts._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit PostsRequest from WPRequest', () => { - expect( posts instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( 'path part setters', () => { - - describe( '.id()', () => { - - it( 'provides a method to set the ID', () => { - expect( posts ).toHaveProperty( 'id' ); - expect( typeof posts.id ).toBe( 'function' ); - } ); - - it( 'should set the ID value in the path', () => { - posts.id( 314159 ); - expect( posts.toString() ).toBe( '/wp-json/wp/v2/posts/314159' ); - } ); - - it( 'accepts ID parameters as strings', () => { - posts.id( '8' ); - expect( posts.toString() ).toBe( '/wp-json/wp/v2/posts/8' ); - } ); - - it( 'should update the supported methods when setting ID', () => { - posts.id( 8 ); - const _supportedMethods = posts._supportedMethods.sort().join( '|' ); - expect( _supportedMethods ).toBe( 'delete|get|head|patch|post|put' ); - } ); - - } ); - - } ); - - it( 'provides expected filter methods', () => { - [ - 'after', - 'before', - 'categories', - 'include', - 'order', - 'page', - 'slug', - 'password', - 'status', - 'sticky', - ].forEach( ( methodName ) => { - expect( typeof posts[ methodName ] ).toBe( 'function' ); - } ); - } ); - - describe( 'URL Generation', () => { - - it( 'should create the URL for retrieving all posts', () => { - const path = posts.toString(); - expect( path ).toBe( '/wp-json/wp/v2/posts' ); - } ); - - it( 'should create the URL for retrieving a specific post', () => { - const path = posts.id( 1337 ).toString(); - expect( path ).toBe( '/wp-json/wp/v2/posts/1337' ); - } ); - - it( 'does not throw an error if a valid numeric ID is specified', () => { - expect( () => { - posts.id( 8 ); - posts.validatePath(); - } ).not.toThrow(); - } ); - - it( 'does not throw an error if a valid numeric ID is specified as a string', () => { - expect( () => { - posts.id( '8' ); - posts.validatePath(); - } ).not.toThrow(); - } ); - - it( 'throws an error if a non-integer numeric string ID is specified', () => { - expect( () => { - posts.id( 4.019 ); - posts.validatePath(); - } ).toThrow(); - } ); - - it( 'throws an error if a non-numeric string ID is specified', () => { - expect( () => { - posts.id( 'wombat' ); - posts.validatePath(); - } ).toThrow(); - } ); - - it( 'should create the URL for retrieving the revisions for a specific post', () => { - const path = posts.id( 1337 ).revisions().toString(); - expect( path ).toBe( '/wp-json/wp/v2/posts/1337/revisions' ); - } ); - - it( 'should create the URL for retrieving a specific revision item', () => { - const path = posts.id( 1337 ).revisions( 2001 ).toString(); - expect( path ).toBe( '/wp-json/wp/v2/posts/1337/revisions/2001' ); - } ); - - it( 'should restrict path changes to a single instance', () => { - posts.id( 2 ); - const newPosts = site.posts().id( 3 ).revisions(); - expect( posts.toString() ).toBe( '/wp-json/wp/v2/posts/2' ); - expect( newPosts.toString() ).toBe( '/wp-json/wp/v2/posts/3/revisions' ); - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/taxonomies.js b/tests/unit/route-handlers/taxonomies.js deleted file mode 100644 index ff94f19a..00000000 --- a/tests/unit/route-handlers/taxonomies.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.taxonomies', () => { - let site; - let taxonomies; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - taxonomies = site.taxonomies(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - taxonomies = site.taxonomies( { - endpoint: '/custom-endpoint/', - } ); - expect( taxonomies._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( taxonomies._options.endpoint ).toBe( '/wp-json/' ); - expect( taxonomies._options.username ).toBe( 'foouser' ); - expect( taxonomies._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( taxonomies.toString() ).toBe( '/wp-json/wp/v2/taxonomies' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( taxonomies ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( taxonomies._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit TaxonomiesRequest from WPRequest', () => { - expect( taxonomies instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( 'path part setters', () => { - - describe( '.taxonomy()', () => { - - it( 'provides a method to set the taxonomy', () => { - expect( taxonomies ).toHaveProperty( 'taxonomy' ); - expect( typeof taxonomies.taxonomy ).toBe( 'function' ); - } ); - - } ); - - } ); - - describe( 'URL Generation', () => { - - it( 'should create the URL for retrieving all taxonomies', () => { - const url = taxonomies.toString(); - expect( url ).toBe( '/wp-json/wp/v2/taxonomies' ); - } ); - - it( 'should create the URL for retrieving a specific taxonomy', () => { - const url = taxonomies.taxonomy( 'category' ).toString(); - expect( url ).toBe( '/wp-json/wp/v2/taxonomies/category' ); - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/types.js b/tests/unit/route-handlers/types.js deleted file mode 100644 index 72ce1802..00000000 --- a/tests/unit/route-handlers/types.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.types', () => { - let site; - let types; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - types = site.types(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - types = site.types( { - endpoint: '/custom-endpoint/', - } ); - expect( types._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( types._options.endpoint ).toBe( '/wp-json/' ); - expect( types._options.username ).toBe( 'foouser' ); - expect( types._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( types.toString() ).toBe( '/wp-json/wp/v2/types' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( types ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( types._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit PostsRequest from WPRequest', () => { - expect( types instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( 'URL Generation', () => { - - it( 'should create the URL for retrieving all types', () => { - const url = types.toString(); - expect( url ).toBe( '/wp-json/wp/v2/types' ); - } ); - - it( 'should create the URL for retrieving a specific term', () => { - const url = types.type( 'some_type' ).toString(); - expect( url ).toBe( '/wp-json/wp/v2/types/some_type' ); - } ); - - } ); - -} ); diff --git a/tests/unit/route-handlers/users.js b/tests/unit/route-handlers/users.js deleted file mode 100644 index 81f3b277..00000000 --- a/tests/unit/route-handlers/users.js +++ /dev/null @@ -1,96 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../../wpapi' ); -const WPRequest = require( '../../../lib/constructors/wp-request' ); - -describe( 'wp.users', () => { - let site; - let users; - - beforeEach( () => { - site = new WPAPI( { - endpoint: '/wp-json', - username: 'foouser', - password: 'barpass', - } ); - users = site.users(); - } ); - - describe( 'constructor', () => { - - it( 'should set any passed-in options', () => { - users = site.users( { - endpoint: '/custom-endpoint/', - } ); - expect( users._options.endpoint ).toBe( '/custom-endpoint/' ); - } ); - - it( 'should initialize _options to the site defaults', () => { - expect( users._options.endpoint ).toBe( '/wp-json/' ); - expect( users._options.username ).toBe( 'foouser' ); - expect( users._options.password ).toBe( 'barpass' ); - } ); - - it( 'should initialize the base path component', () => { - expect( users.toString() ).toBe( '/wp-json/wp/v2/users' ); - } ); - - it( 'should set a default _supportedMethods array', () => { - expect( users ).toHaveProperty( '_supportedMethods' ); - expect( Array.isArray( users._supportedMethods ) ).toBe( true ); - } ); - - it( 'should inherit UsersRequest from WPRequest', () => { - expect( users instanceof WPRequest ).toBe( true ); - } ); - - } ); - - describe( '.me()', () => { - - it( 'sets the path to users/me', () => { - users.me(); - expect( users.toString() ).toBe( '/wp-json/wp/v2/users/me' ); - } ); - - } ); - - describe( '.id()', () => { - - it( 'is defined', () => { - expect( users ).toHaveProperty( 'id' ); - } ); - - it( 'is a function', () => { - expect( typeof users.id ).toBe( 'function' ); - } ); - - it( 'sets the path ID to the passed-in value', () => { - users.id( 2501 ); - expect( users.toString() ).toBe( '/wp-json/wp/v2/users/2501' ); - } ); - - } ); - - describe( 'prototype.toString', () => { - - it( 'should create the URL for retrieving all users', () => { - const url = users.toString(); - expect( url ).toBe( '/wp-json/wp/v2/users' ); - } ); - - it( 'should create the URL for retrieving the current user', () => { - const url = users.me().toString(); - expect( url ).toBe( '/wp-json/wp/v2/users/me' ); - } ); - - it( 'should create the URL for retrieving a specific user by ID', () => { - const url = users.id( 1337 ).toString(); - const _supportedMethods = users._supportedMethods.sort().join( '|' ); - expect( url ).toBe( '/wp-json/wp/v2/users/1337' ); - expect( _supportedMethods ).toBe( 'delete|get|head|patch|post|put' ); - } ); - - } ); - -} ); diff --git a/tests/unit/wpapi.js b/tests/unit/wpapi.js deleted file mode 100644 index 8f3a5691..00000000 --- a/tests/unit/wpapi.js +++ /dev/null @@ -1,604 +0,0 @@ -'use strict'; - -const WPAPI = require( '../../' ); - -// Constructors, for use with instanceof checks -const WPRequest = require( '../../lib/constructors/wp-request' ); - -describe( 'WPAPI', () => { - - let site; - - beforeEach( () => { - site = new WPAPI( { endpoint: 'endpoint/url' } ); - } ); - - describe( 'constructor', () => { - - it( 'enforces new', () => { - const site1 = new WPAPI( { endpoint: '/' } ); - expect( site1 instanceof WPAPI ).toBe( true ); - const site2 = WPAPI( { endpoint: '/' } ); - expect( site2 instanceof WPAPI ).toBe( true ); - } ); - - it( 'throws an error if no endpoint is provided', () => { - expect( () => { - new WPAPI( { endpoint: '/' } ); - } ).not.toThrow(); - expect( () => { - new WPAPI(); - } ).toThrow(); - } ); - - it( 'throws an error if a non-string endpoint is provided', () => { - expect( () => { - new WPAPI( { endpoint: 42 } ); - } ).toThrow(); - expect( () => { - new WPAPI( { endpoint: [] } ); - } ).toThrow(); - expect( () => { - new WPAPI( { endpoint: { lob: 'ster' } } ); - } ).toThrow(); - } ); - - it( 'sets options on an instance variable', () => { - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - username: 'fyodor', - password: 'dostoyevsky', - } ); - expect( site._options.endpoint ).toBe( 'http://some.url.com/wp-json/' ); - expect( site._options.username ).toBe( 'fyodor' ); - expect( site._options.password ).toBe( 'dostoyevsky' ); - } ); - - it( 'activates authentication when credentials are provided', () => { - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - username: 'fyodor', - password: 'dostoyevsky', - } ); - expect( site._options.username ).toBe( 'fyodor' ); - expect( site._options.password ).toBe( 'dostoyevsky' ); - expect( site._options.auth ).toBe( true ); - } ); - - describe( 'custom HTTP transport methods', () => { - - beforeEach( () => { - WPAPI.transport = { - get: jest.fn().mockImplementation( () => {} ), - post: jest.fn().mockImplementation( () => {} ), - put: jest.fn().mockImplementation( () => {} ), - }; - } ); - - it( 'can be set for an individual HTTP action', () => { - const customGet = jest.fn(); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - transport: { - get: customGet, - }, - } ); - const query = site.root( '' ); - query.get(); - expect( customGet ).toHaveBeenCalledWith( query ); - expect( WPAPI.transport.get ).not.toHaveBeenCalled(); - WPAPI.transport.get.mockRestore(); - } ); - - it( 'can extend the default HTTP transport methods', () => { - const customGet = jest.fn( ( ...args ) => { - WPAPI.transport.get.apply( null, args ); - } ); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - transport: { - get: customGet, - }, - } ); - const query = site.root( '' ); - query.get(); - expect( customGet ).toHaveBeenCalledWith( query ); - expect( WPAPI.transport.get ).toHaveBeenCalledWith( query ); - WPAPI.transport.get.mockRestore(); - } ); - - it( 'can be set for multiple HTTP actions', () => { - const customPost = jest.fn(); - const customPut = jest.fn(); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - transport: { - post: customPost, - put: customPut, - }, - } ); - const query = site.root( 'a-resource' ); - const data = {}; - query.create( data ); - expect( customPost ).toHaveBeenCalledWith( query, data ); - expect( WPAPI.transport.post ).not.toHaveBeenCalled(); - query.update( data ); - expect( customPut ).toHaveBeenCalledWith( query, data ); - expect( WPAPI.transport.put ).not.toHaveBeenCalled(); - WPAPI.transport.post.mockRestore(); - WPAPI.transport.put.mockRestore(); - } ); - - it( 'only apply to a specific WPAPI instance', () => { - const customGet = jest.fn(); - const site = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - transport: { - get: customGet, - }, - } ); - const site2 = new WPAPI( { - endpoint: 'http://some.url.com/wp-json', - } ); - expect( site ).not.toBe( site2 ); - const query = site2.root( '' ); - query.get(); - expect( WPAPI.transport.get ).toHaveBeenCalledWith( query ); - expect( customGet ).not.toHaveBeenCalled(); - WPAPI.transport.get.mockRestore(); - } ); - - } ); - - } ); - - describe( '.site() constructor method', () => { - - it( 'is a function', () => { - expect( WPAPI ).toHaveProperty( 'site' ); - expect( typeof WPAPI.site ).toBe( 'function' ); - } ); - - it( 'creates and returns a new WPAPI instance', () => { - const site = WPAPI.site( 'endpoint/url' ); - expect( site instanceof WPAPI ).toBe( true ); - expect( site._options.endpoint ).toBe( 'endpoint/url/' ); - } ); - - it( 'can take a routes configuration object to bootstrap the returned instance', () => { - const site = WPAPI.site( 'endpoint/url', { - '/wp/v2/posts': { - namespace: 'wp/v2', - methods: [ 'GET' ], - endpoints: [ { - methods: [ 'GET' ], - args: { - filter: {}, - }, - } ], - }, - } ); - expect( site instanceof WPAPI ).toBe( true ); - expect( typeof site.posts ).toBe( 'function' ); - expect( site ).not.toHaveProperty( 'comments' ); - expect( site.posts() ).not.toHaveProperty( 'id' ); - expect( typeof site.posts().filter ).toBe( 'function' ); - expect( site.posts().toString() ).toBe( 'endpoint/url/wp/v2/posts' ); - } ); - - } ); - - describe( '.prototype', () => { - - describe( '.namespace()', () => { - - it( 'is a function', () => { - expect( site ).toHaveProperty( 'namespace' ); - expect( typeof site.namespace ).toBe( 'function' ); - } ); - - it( 'returns a namespace object with relevant endpoint handler methods', () => { - const wpV2 = site.namespace( 'wp/v2' ); - // Spot check - expect( typeof wpV2 ).toBe( 'object' ); - expect( wpV2 ).toHaveProperty( 'posts' ); - expect( typeof wpV2.posts ).toBe( 'function' ); - expect( wpV2 ).toHaveProperty( 'comments' ); - expect( typeof wpV2.comments ).toBe( 'function' ); - } ); - - it( 'passes options from the parent WPAPI instance to the namespaced handlers', () => { - site.auth( { - username: 'u', - password: 'p', - } ); - const pages = site.namespace( 'wp/v2' ).pages(); - expect( typeof pages._options ).toBe( 'object' ); - expect( pages._options ).toHaveProperty( 'username' ); - expect( pages._options.username ).toBe( 'u' ); - expect( pages._options ).toHaveProperty( 'password' ); - expect( pages._options.password ).toBe( 'p' ); - } ); - - it( 'permits the namespace to be stored in a variable without disrupting options', () => { - site.auth( { - username: 'u', - password: 'p', - } ); - const wpV2 = site.namespace( 'wp/v2' ); - const pages = wpV2.pages(); - expect( typeof pages._options ).toBe( 'object' ); - expect( pages._options ).toHaveProperty( 'username' ); - expect( pages._options.username ).toBe( 'u' ); - expect( pages._options ).toHaveProperty( 'password' ); - expect( pages._options.password ).toBe( 'p' ); - } ); - - it( 'throws an error when provided no namespace', () => { - expect( () => { - site.namespace(); - } ).toThrow(); - } ); - - it( 'throws an error when provided an unregistered namespace', () => { - expect( () => { - site.namespace( 'foo/baz' ); - } ).toThrow(); - } ); - - } ); - - describe( '.bootstrap()', () => { - - beforeEach( () => { - site.bootstrap( { - '/myplugin/v1/authors/(?P[\\w-]+)': { - namespace: 'myplugin/v1', - methods: [ 'GET', 'POST' ], - endpoints: [ { - methods: [ 'GET' ], - args: { - name: {}, - }, - } ], - }, - '/wp/v2/customendpoint/(?P[\\w-]+)': { - namespace: 'wp/v2', - methods: [ 'GET', 'POST' ], - endpoints: [ { - methods: [ 'GET' ], - args: { - parent: {}, - }, - } ], - }, - } ); - } ); - - it( 'is a function', () => { - expect( site ).toHaveProperty( 'bootstrap' ); - expect( typeof site.bootstrap ).toBe( 'function' ); - } ); - - it( 'is chainable', () => { - expect( site.bootstrap() ).toBe( site ); - } ); - - it( 'creates handlers for all provided route definitions', () => { - expect( typeof site.namespace( 'myplugin/v1' ) ).toBe( 'object' ); - expect( site.namespace( 'myplugin/v1' ) ).toHaveProperty( 'authors' ); - expect( typeof site.namespace( 'myplugin/v1' ).authors ).toBe( 'function' ); - expect( typeof site.namespace( 'wp/v2' ) ).toBe( 'object' ); - expect( site.namespace( 'wp/v2' ) ).toHaveProperty( 'customendpoint' ); - expect( typeof site.namespace( 'wp/v2' ).customendpoint ).toBe( 'function' ); - } ); - - it( 'properly assigns setter methods for detected path parts', () => { - const thingHandler = site.customendpoint(); - expect( thingHandler ).toHaveProperty( 'thing' ); - expect( typeof thingHandler.thing ).toBe( 'function' ); - expect( thingHandler.thing( 'foobar' ).toString() ).toBe( 'endpoint/url/wp/v2/customendpoint/foobar' ); - } ); - - it( 'assigns any mixins for detected GET arguments for custom namespace handlers', () => { - const authorsHandler = site.namespace( 'myplugin/v1' ).authors(); - expect( authorsHandler ).toHaveProperty( 'name' ); - expect( typeof authorsHandler.name ).toBe( 'function' ); - const customEndpoint = site.customendpoint(); - expect( customEndpoint ).toHaveProperty( 'parent' ); - expect( typeof customEndpoint.parent ).toBe( 'function' ); - } ); - - it( 'assigns handlers for wp/v2 routes to the instance object itself', () => { - expect( site ).toHaveProperty( 'customendpoint' ); - expect( typeof site.customendpoint ).toBe( 'function' ); - expect( site.namespace( 'wp/v2' ).customendpoint ).toBe( site.customendpoint ); - } ); - - } ); - - describe( '.transport()', () => { - - it( 'is defined', () => { - expect( site ).toHaveProperty( 'transport' ); - } ); - - it( 'is a function', () => { - expect( typeof site.transport ).toBe( 'function' ); - } ); - - it( 'is chainable', () => { - expect( site.transport() ).toBe( site ); - } ); - - it( 'sets transport methods on the instance', () => { - const customGet = jest.fn(); - site.transport( { - get: customGet, - } ); - const query = site.root( '' ); - query.get(); - expect( customGet ).toHaveBeenCalledWith( query ); - } ); - - it( 'does not impact or overwrite unspecified transport methods', () => { - const originalMethods = { - ...site._options.transport, - }; - site.transport( { - get() {}, - put() {}, - } ); - const newMethods = { - ...site._options.transport, - }; - expect( newMethods.delete ).toBe( originalMethods.delete ); - expect( newMethods.post ).toBe( originalMethods.post ); - - expect( newMethods.get ).not.toBe( originalMethods.get ); - expect( newMethods.put ).not.toBe( originalMethods.put ); - } ); - - } ); - - describe( '.url()', () => { - - it( 'is defined', () => { - expect( site ).toHaveProperty( 'url' ); - expect( typeof site.url ).toBe( 'function' ); - } ); - - it( 'creates a basic WPRequest object bound to the provided URL', () => { - const request = site.url( 'http://new-endpoint.com/' ); - expect( request._options ).toHaveProperty( 'endpoint' ); - expect( request._options.endpoint ).toBe( 'http://new-endpoint.com/' ); - expect( request._options ).not.toHaveProperty( 'identifier' ); - } ); - - } ); - - describe( '.root()', () => { - - beforeEach( () => { - site = new WPAPI( { endpoint: 'http://my.site.com/wp-json' } ); - } ); - - it( 'is defined', () => { - expect( site ).toHaveProperty( 'root' ); - expect( typeof site.root ).toBe( 'function' ); - } ); - - it( 'creates a get request against the root endpoint', () => { - const pathRequest = site.root( 'some/collection/endpoint' ); - expect( pathRequest instanceof WPRequest ).toBe( true ); - } ); - - it( 'inherits options from the parent WPAPI instance', () => { - const site = new WPAPI( { - endpoint: 'http://cat.website.com/', - } ); - const request = site.root( 'custom-path' ); - expect( request._options ).toHaveProperty( 'endpoint' ); - expect( request._options.endpoint ).toBe( 'http://cat.website.com/' ); - } ); - - } ); - - describe( '.auth()', () => { - - beforeEach( () => { - site = new WPAPI( { endpoint: 'http://my.site.com/wp-json' } ); - } ); - - it( 'is defined', () => { - expect( site ).toHaveProperty( 'auth' ); - expect( typeof site.auth ).toBe( 'function' ); - } ); - - it( 'activates authentication for the site', () => { - expect( site._options ).not.toHaveProperty( 'auth' ); - site.auth(); - expect( site._options ).toHaveProperty( 'auth' ); - expect( site._options.auth ).toBe( true ); - } ); - - it( 'sets the username and password when provided in an object', () => { - site.auth( { - username: 'user1', - password: 'pass1', - } ); - expect( site._options ).toHaveProperty( 'username' ); - expect( site._options ).toHaveProperty( 'password' ); - expect( site._options.username ).toBe( 'user1' ); - expect( site._options.password ).toBe( 'pass1' ); - expect( site._options ).toHaveProperty( 'auth' ); - expect( site._options.auth ).toBe( true ); - } ); - - it( 'can update previously-set usernames and passwords', () => { - site.auth( { - username: 'user1', - password: 'pass1', - } ).auth( { - username: 'admin', - password: 'sandwich', - } ); - expect( site._options ).toHaveProperty( 'username' ); - expect( site._options ).toHaveProperty( 'password' ); - expect( site._options.username ).toBe( 'admin' ); - expect( site._options.password ).toBe( 'sandwich' ); - expect( site._options ).toHaveProperty( 'auth' ); - expect( site._options.auth ).toBe( true ); - } ); - - it( 'sets the nonce when provided in an object', () => { - site.auth( { - nonce: 'somenonce', - } ); - expect( site._options ).toHaveProperty( 'nonce' ); - expect( site._options.nonce ).toBe( 'somenonce' ); - expect( site._options ).toHaveProperty( 'auth' ); - expect( site._options.auth ).toBe( true ); - } ); - - it( 'can update nonce credentials', () => { - site.auth( { - nonce: 'somenonce', - } ).auth( { - nonce: 'refreshednonce', - } ); - expect( site._options ).toHaveProperty( 'nonce' ); - expect( site._options.nonce ).toBe( 'refreshednonce' ); - expect( site._options ).toHaveProperty( 'auth' ); - expect( site._options.auth ).toBe( true ); - } ); - - it( 'passes authentication status to all subsequently-instantiated handlers', () => { - site.auth( { - username: 'user', - password: 'pass', - } ); - const req = site.root( '' ); - expect( req ).toHaveProperty( '_options' ); - expect( typeof req._options ).toBe( 'object' ); - expect( req._options ).toHaveProperty( 'username' ); - expect( req._options.username ).toBe( 'user' ); - expect( req._options ).toHaveProperty( 'password' ); - expect( req._options.password ).toBe( 'pass' ); - expect( req._options ).toHaveProperty( 'password' ); - expect( req._options.auth ).toBe( true ); - } ); - - } ); - - describe( '.setHeaders()', () => { - - beforeEach( () => { - site = new WPAPI( { endpoint: 'http://my.site.com/wp-json' } ); - } ); - - it( 'is defined', () => { - expect( site ).toHaveProperty( 'setHeaders' ); - expect( typeof site.setHeaders ).toBe( 'function' ); - } ); - - it( 'initializes site-wide headers object if called with no arguments', () => { - expect( site._options ).not.toHaveProperty( 'headers' ); - site.setHeaders(); - expect( site._options ).toHaveProperty( 'headers' ); - expect( site._options.headers ).toEqual( {} ); - } ); - - it( 'sets site-wide headers when provided a name-value pair', () => { - site.setHeaders( 'Accept-Language', 'en-US' ); - expect( site._options ).toHaveProperty( 'headers' ); - expect( site._options.headers ).toEqual( { - 'Accept-Language': 'en-US', - } ); - } ); - - it( 'sets site-wide headers when provided an object of header name-value pairs', () => { - site.setHeaders( { - 'Accept-Language': 'en-CA', - Authorization: 'Bearer sometoken', - } ); - expect( site._options ).toHaveProperty( 'headers' ); - expect( site._options.headers ).toEqual( { - 'Accept-Language': 'en-CA', - Authorization: 'Bearer sometoken', - } ); - } ); - - it( 'passes headers to all subsequently-instantiated handlers', () => { - site.setHeaders( { - 'Accept-Language': 'en-IL', - Authorization: 'Bearer chicagostylepizza', - } ); - const req = site.root( '' ); - expect( req ).toHaveProperty( '_options' ); - expect( typeof req._options ).toBe( 'object' ); - expect( req._options ).toHaveProperty( 'headers' ); - expect( req._options.headers ).toEqual( { - 'Accept-Language': 'en-IL', - Authorization: 'Bearer chicagostylepizza', - } ); - } ); - - } ); - - describe( '.registerRoute()', () => { - - it( 'is a function', () => { - expect( site ).toHaveProperty( 'registerRoute' ); - expect( typeof site.registerRoute ).toBe( 'function' ); - } ); - - } ); - - } ); - - describe( 'instance has endpoint accessors', () => { - - it( 'for the media endpoint', () => { - expect( site ).toHaveProperty( 'media' ); - expect( typeof site.media ).toBe( 'function' ); - } ); - - it( 'for the pages endpoint', () => { - expect( site ).toHaveProperty( 'pages' ); - expect( typeof site.pages ).toBe( 'function' ); - } ); - - it( 'for the posts endpoint', () => { - expect( site ).toHaveProperty( 'posts' ); - expect( typeof site.posts ).toBe( 'function' ); - } ); - - it( 'for the taxonomies endpoint', () => { - expect( site ).toHaveProperty( 'taxonomies' ); - expect( typeof site.taxonomies ).toBe( 'function' ); - } ); - - it( 'for the categories endpoint', () => { - expect( site ).toHaveProperty( 'categories' ); - expect( typeof site.categories ).toBe( 'function' ); - } ); - - it( 'for the tags endpoint', () => { - expect( site ).toHaveProperty( 'tags' ); - expect( typeof site.tags ).toBe( 'function' ); - } ); - - it( 'for the types endpoint', () => { - expect( site ).toHaveProperty( 'types' ); - expect( typeof site.types ).toBe( 'function' ); - } ); - - it( 'for the users endpoint', () => { - expect( site ).toHaveProperty( 'users' ); - expect( typeof site.users ).toBe( 'function' ); - } ); - - } ); - -} ); diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index 77f38fe3..00000000 --- a/webpack.config.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -const { join } = require( 'path' ); - -module.exports = { - entry: { - wpapi: './fetch', - 'wpapi-superagent': './superagent', - }, - - // Use browser builtins instead of Node packages where appropriate. - externals: { - 'node-fetch': 'fetch', - 'form-data': 'FormData', - }, - - node: { - fs: 'empty', - }, - - mode: 'development', - - devtool: 'cheap-module-source-map', - - stats: { - all: false, - assets: true, - colors: true, - errors: true, - performance: true, - timings: true, - warnings: true, - }, - - output: { - path: join( process.cwd(), 'browser' ), - filename: '[name].js', - library: 'WPAPI', - libraryTarget: 'umd', - }, - - module: { - rules: [ - { - test: /\.js$/, - exclude: /(node_modules)/, - loader: require.resolve( 'babel-loader' ), - options: { - presets: [ '@babel/preset-env' ], - plugins: [ '@babel/plugin-proposal-object-rest-spread' ], - // Cache compilation results in ./node_modules/.cache/babel-loader/ - cacheDirectory: true, - }, - }, - ], - }, - -}; diff --git a/webpack.config.minified.js b/webpack.config.minified.js deleted file mode 100644 index 28c3ed3e..00000000 --- a/webpack.config.minified.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -const { BundleAnalyzerPlugin } = require( 'webpack-bundle-analyzer' ); - -const config = require( './webpack.config' ); - -// Re-use normal Webpack build config, just adding minification -module.exports = { - ...config, - - mode: 'production', - - devtool: 'hidden-source-map', - - output: { - ...config.output, - filename: '[name].min.js', - }, - - optimization: { - noEmitOnErrors: true, - }, - - plugins: [ - ...( config.plugins || [] ), - ], -}; - -// Conditionally opt-in to stats reporting UI. -if ( process.argv.includes( '--stats' ) ) { - module.exports.plugins.push( new BundleAnalyzerPlugin() ); -} diff --git a/wpapi-computed-methods.jsdoc b/wpapi-computed-methods.jsdoc deleted file mode 100644 index b8263ea4..00000000 --- a/wpapi-computed-methods.jsdoc +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Start a request against /posts endpoints - * - * @method posts - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /pages endpoints - * - * @method pages - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /types endpoints - * - * @method types - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /comments endpoints - * - * @method comments - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /taxonomies endpoints - * - * @method taxonomies - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /tags endpoint - * - * @method tags - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /categories endpoint - * - * @method categories - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /statuses endpoints - * - * @method statuses - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /users endpoints - * - * @method users - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /media endpoints - * - * @method media - * @memberof! WPAPI - * @returns {WPRequest} - */ -/** - * Start a request against /settings endpoint - * - * @method settings - * @memberof! WPAPI - * @returns {WPRequest} - */ diff --git a/wpapi.js b/wpapi.js deleted file mode 100644 index c3e52325..00000000 --- a/wpapi.js +++ /dev/null @@ -1,394 +0,0 @@ -/** - * A WP REST API client for Node.js - * - * @example - * var wp = new WPAPI({ endpoint: 'http://src.wordpress-develop.dev/wp-json' }); - * wp.posts().then(function( posts ) { - * console.log( posts ); - * }).catch(function( err ) { - * console.error( err ); - * }); - * - * @license MIT - }) - */ -'use strict'; - -const objectReduce = require( './lib/util/object-reduce' ); - -// This JSON file provides enough data to create handler methods for all valid -// API routes in WordPress 4.7 -const defaultRoutes = require( './lib/data/default-routes.json' ); -const buildRouteTree = require( './lib/route-tree' ).build; -const generateEndpointFactories = require( './lib/endpoint-factories' ).generate; - -// The default endpoint factories will be lazy-loaded by parsing the default -// route tree data if a default-mode WPAPI instance is created (i.e. one that -// is to be bootstrapped with the handlers for all of the built-in routes) -let defaultEndpointFactories; - -// Constant used to detect first-party WordPress REST API routes -const apiDefaultNamespace = 'wp/v2'; - -// Pull in base module constructors -const WPRequest = require( './lib/constructors/wp-request' ); - -/** - * Construct a REST API client instance object to create - * - * @constructor WPAPI - * @param {Object} options An options hash to configure the instance - * @param {String} options.endpoint The URI for a WP-API endpoint - * @param {String} [options.username] A WP-API Basic Auth username - * @param {String} [options.password] A WP-API Basic Auth password - * @param {String} [options.nonce] A WP nonce for use with cookie authentication - * @param {Object} [options.routes] A dictionary of API routes with which to - * bootstrap the WPAPI instance: the instance will - * be initialized with default routes only - * if this property is omitted - * @param {String} [options.transport] An optional dictionary of HTTP transport - * methods (.get, .post, .put, .delete, .head) - * to use instead of the defaults, e.g. to use - * a different HTTP library than superagent - */ -function WPAPI( options ) { - - // Enforce `new` - if ( this instanceof WPAPI === false ) { - return new WPAPI( options ); - } - - if ( typeof options.endpoint !== 'string' ) { - throw new Error( 'options hash must contain an API endpoint URL string' ); - } - - // Dictionary to be filled by handlers for default namespaces - this._ns = {}; - - this._options = { - // Ensure trailing slash on endpoint URI - endpoint: options.endpoint.replace( /\/?$/, '/' ), - }; - - // If any authentication credentials were provided, assign them now - if ( options && ( options.username || options.password || options.nonce ) ) { - this.auth( options ); - } - - return this - // Configure custom HTTP transport methods, if provided - .transport( options.transport ) - // Bootstrap with a specific routes object, if provided - .bootstrap( options && options.routes ); -} - -/** - * Set custom transport methods to use when making HTTP requests against the API - * - * Pass an object with a function for one or many of "get", "post", "put", - * "delete" and "head" and that function will be called when making that type - * of request. The provided transport functions should take a WPRequest handler - * instance (_e.g._ the result of a `wp.posts()...` chain or any other chaining - * request handler) as their first argument; a `data` object as their second - * argument (for POST, PUT and DELETE requests); and an optional callback as - * their final argument. Transport methods should invoke the callback with the - * response data (or error, as appropriate), and should also return a Promise. - * - * @example showing how a cache hit (keyed by URI) could short-circuit a get request - * - * var site = new WPAPI({ - * endpoint: 'http://my-site.com/wp-json' - * }); - * - * // Overwrite the GET behavior to inject a caching layer - * site.transport({ - * get: function( wpreq ) { - * var result = cache[ wpreq ]; - * // If a cache hit is found, return it via the same promise - * // signature as that of the default transport method - * if ( result ) { - * return Promise.resolve( result ); - * } - * - * // Delegate to default transport if no cached data was found - * return this.constructor.transport.get( wpreq ).then(function( result ) { - * cache[ wpreq ] = result; - * return result; - * }); - * } - * }); - * - * This is advanced behavior; you will only need to utilize this functionality - * if your application has very specific HTTP handling or caching requirements. - * Refer to the "http-transport" module within this application for the code - * implementing the built-in transport methods. - * - * @memberof! WPAPI - * @method transport - * @chainable - * @param {Object} transport A dictionary of HTTP transport methods - * @param {Function} [transport.get] The function to use for GET requests - * @param {Function} [transport.post] The function to use for POST requests - * @param {Function} [transport.put] The function to use for PUT requests - * @param {Function} [transport.delete] The function to use for DELETE requests - * @param {Function} [transport.head] The function to use for HEAD requests - * @returns {WPAPI} The WPAPI instance, for chaining - */ -WPAPI.prototype.transport = function( transport ) { - // Local reference to avoid need to reference via `this` inside forEach - const _options = this._options; - - // Attempt to use the default transport if no override was provided - if ( ! _options.transport ) { - _options.transport = this.constructor.transport ? - Object.create( this.constructor.transport ) : - {}; - } - - // Whitelist the methods that may be applied - [ 'get', 'head', 'post', 'put', 'delete' ].forEach( ( key ) => { - if ( transport && transport[ key ] ) { - _options.transport[ key ] = transport[ key ]; - } - } ); - - return this; -}; - -/** - * Generate a request against a completely arbitrary endpoint, with no assumptions about - * or mutation of path, filtering, or query parameters. This request is not restricted to - * the endpoint specified during WPAPI object instantiation. - * - * @example - * Generate a request to the explicit URL "http://your.website.com/wp-json/some/custom/path" - * - * wp.url( 'http://your.website.com/wp-json/some/custom/path' ).get()... - * - * @memberof! WPAPI - * @param {String} url The URL to request - * @returns {WPRequest} A WPRequest object bound to the provided URL - */ -WPAPI.prototype.url = function( url ) { - return new WPRequest( { - ...this._options, - endpoint: url, - } ); -}; - -/** - * Generate a query against an arbitrary path on the current endpoint. This is useful for - * requesting resources at custom WP-API endpoints, such as WooCommerce's `/products`. - * - * @memberof! WPAPI - * @param {String} [relativePath] An endpoint-relative path to which to bind the request - * @returns {WPRequest} A request object - */ -WPAPI.prototype.root = function( relativePath ) { - relativePath = relativePath || ''; - const options = { - ...this._options, - }; - // Request should be - const request = new WPRequest( options ); - - // Set the path template to the string passed in - request._path = { '0': relativePath }; - - return request; -}; - -/** - * Set the default headers to use for all HTTP requests created from this WPAPI - * site instance. Accepts a header name and its associated value as two strings, - * or multiple headers as an object of name-value pairs. - * - * @example Set a single header to be used by all requests to this site - * - * site.setHeaders( 'Authorization', 'Bearer trustme' )... - * - * @example Set multiple headers to be used by all requests to this site - * - * site.setHeaders({ - * Authorization: 'Bearer comeonwereoldfriendsright', - * 'Accept-Language': 'en-CA' - * })... - * - * @memberof! WPAPI - * @since 1.1.0 - * @chainable - * @param {String|Object} headers The name of the header to set, or an object of - * header names and their associated string values - * @param {String} [value] The value of the header being set - * @returns {WPAPI} The WPAPI site handler instance, for chaining - */ -WPAPI.prototype.setHeaders = WPRequest.prototype.setHeaders; - -/** - * Set the authentication to use for a WPAPI site handler instance. Accepts basic - * HTTP authentication credentials (string username & password) or a Nonce (for - * cookie authentication) by default; may be overloaded to accept OAuth credentials - * in the future. - * - * @example Basic Authentication - * - * site.auth({ - * username: 'admin', - * password: 'securepass55' - * })... - * - * @example Cookie/Nonce Authentication - * - * site.auth({ - * nonce: 'somenonce' - * })... - * - * @memberof! WPAPI - * @method - * @chainable - * @param {Object} credentials An authentication credentials object - * @param {String} [credentials.username] A WP-API Basic HTTP Authentication username - * @param {String} [credentials.password] A WP-API Basic HTTP Authentication password - * @param {String} [credentials.nonce] A WP nonce for use with cookie authentication - * @returns {WPAPI} The WPAPI site handler instance, for chaining - */ -WPAPI.prototype.auth = WPRequest.prototype.auth; - -// Apply the registerRoute method to the prototype -WPAPI.prototype.registerRoute = require( './lib/wp-register-route' ); - -/** - * Deduce request methods from a provided API root JSON response object's - * routes dictionary, and assign those methods to the current instance. If - * no routes dictionary is provided then the instance will be bootstrapped - * with route handlers for the default API endpoints only. - * - * This method is called automatically during WPAPI instance creation. - * - * @memberof! WPAPI - * @chainable - * @param {Object} routes The "routes" object from the JSON object returned - * from the root API endpoint of a WP site, which should - * be a dictionary of route definition objects keyed by - * the route's regex pattern - * @returns {WPAPI} The bootstrapped WPAPI client instance (for chaining or assignment) - */ -WPAPI.prototype.bootstrap = function( routes ) { - let routesByNamespace; - let endpointFactoriesByNamespace; - - if ( ! routes ) { - // Auto-generate default endpoint factories if they are not already available - if ( ! defaultEndpointFactories ) { - routesByNamespace = buildRouteTree( defaultRoutes ); - defaultEndpointFactories = generateEndpointFactories( routesByNamespace ); - } - endpointFactoriesByNamespace = defaultEndpointFactories; - } else { - routesByNamespace = buildRouteTree( routes ); - endpointFactoriesByNamespace = generateEndpointFactories( routesByNamespace ); - } - - // For each namespace for which routes were identified, store the generated - // route handlers on the WPAPI instance's private _ns dictionary. These namespaced - // handler methods can be accessed by calling `.namespace( str )` on the - // client instance and passing a registered namespace string. - // Handlers for default (wp/v2) routes will also be assigned to the WPAPI - // client instance object itself, for brevity. - return objectReduce( endpointFactoriesByNamespace, ( wpInstance, endpointFactories, namespace ) => { - - // Set (or augment) the route handler factories for this namespace. - wpInstance._ns[ namespace ] = objectReduce( - endpointFactories, - ( nsHandlers, handlerFn, methodName ) => { - nsHandlers[ methodName ] = handlerFn; - return nsHandlers; - }, - wpInstance._ns[ namespace ] || { - // Create all namespace dictionaries with a direct reference to the main WPAPI - // instance's _options property so that things like auth propagate properly - _options: wpInstance._options, - } - ); - - // For the default namespace, e.g. "wp/v2" at the time this comment was - // written, ensure all methods are assigned to the root client object itself - // in addition to the private _ns dictionary: this is done so that these - // methods can be called with e.g. `wp.posts()` and not the more verbose - // `wp.namespace( 'wp/v2' ).posts()`. - if ( namespace === apiDefaultNamespace ) { - Object.keys( wpInstance._ns[ namespace ] ).forEach( ( methodName ) => { - wpInstance[ methodName ] = wpInstance._ns[ namespace ][ methodName ]; - } ); - } - - return wpInstance; - }, this ); -}; - -/** - * Access API endpoint handlers from a particular API namespace object - * - * @example - * - * wp.namespace( 'myplugin/v1' ).author()... - * - * // Default WP endpoint handlers are assigned to the wp instance itself. - * // These are equivalent: - * wp.namespace( 'wp/v2' ).posts()... - * wp.posts()... - * - * @memberof! WPAPI - * @param {string} namespace A namespace string - * @returns {Object} An object of route endpoint handler methods for the - * routes within the specified namespace - */ -WPAPI.prototype.namespace = function( namespace ) { - if ( ! this._ns[ namespace ] ) { - throw new Error( 'Error: namespace ' + namespace + ' is not recognized' ); - } - return this._ns[ namespace ]; -}; - -/** - * Convenience method for making a new WPAPI instance for a given API root - * - * @example - * These are equivalent: - * - * var wp = new WPAPI({ endpoint: 'http://my.blog.url/wp-json' }); - * var wp = WPAPI.site( 'http://my.blog.url/wp-json' ); - * - * `WPAPI.site` can take an optional API root response JSON object to use when - * bootstrapping the client's endpoint handler methods: if no second parameter - * is provided, the client instance is assumed to be using the default API - * with no additional plugins and is initialized with handlers for only those - * default API routes. - * - * @example - * These are equivalent: - * - * // {...} means the JSON output of http://my.blog.url/wp-json - * var wp = new WPAPI({ - * endpoint: 'http://my.blog.url/wp-json', - * json: {...} - * }); - * var wp = WPAPI.site( 'http://my.blog.url/wp-json', {...} ); - * - * @memberof! WPAPI - * @static - * @param {String} endpoint The URI for a WP-API endpoint - * @param {Object} routes The "routes" object from the JSON object returned - * from the root API endpoint of a WP site, which should - * be a dictionary of route definition objects keyed by - * the route's regex pattern - * @returns {WPAPI} A new WPAPI instance, bound to the provided endpoint - */ -WPAPI.site = ( endpoint, routes ) => { - return new WPAPI( { - endpoint: endpoint, - routes: routes, - } ); -}; - -module.exports = WPAPI; diff --git a/wpapi.zip b/wpapi.zip new file mode 100644 index 00000000..0706f0e4 Binary files /dev/null and b/wpapi.zip differ