├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── authors.txt ├── demo ├── index.html ├── package.json └── server.js ├── lib ├── handlers │ ├── authorsById.js │ ├── fetchById.js │ ├── fetchByIndices.js │ ├── mediaById.js │ ├── postsById.js │ ├── postsByIndices.js │ ├── postsByTerm.js │ ├── taxonomiesById.js │ ├── termsById.js │ ├── termsByIndices.js │ └── termsByPost.js └── wordpressRouter.js ├── package.json ├── src ├── handlers │ ├── authorsById.js │ ├── fetchById.js │ ├── fetchByIndices.js │ ├── mediaById.js │ ├── postsById.js │ ├── postsByIndices.js │ ├── postsByTerm.js │ ├── taxonomiesById.js │ ├── termsById.js │ ├── termsByIndices.js │ └── termsByPost.js └── wordpressRouter.js └── tests ├── fixtures └── responses │ ├── postsByIdMultiple │ ├── postsByIdSingle │ ├── postsByTermSlug │ ├── recentPostsBasic │ ├── taxonomiesMeta │ └── taxonomyTermsByIndex └── wordpressRouter.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true, 5 | "es6": true 6 | }, 7 | 8 | "ecmaFeatures": { 9 | "arrowFunctions": true, 10 | "binaryLiterals": true, 11 | "blockBindings": true, 12 | "classes": true, 13 | "defaultParams": true, 14 | "destructuring": true, 15 | "forOf": true, 16 | "generators": true, 17 | "modules": true, 18 | "objectLiteralComputedProperties": true, 19 | "objectLiteralDuplicateProperties": true, 20 | "objectLiteralShorthandMethods": true, 21 | "objectLiteralShorthandProperties": true, 22 | "octalLiterals": true, 23 | "regexUFlag": true, 24 | "regexYFlag": true, 25 | "spread": true, 26 | "superInFunctions": true, 27 | "templateStrings": true, 28 | "unicodeCodePointEscapes": true, 29 | "globalReturn": true, 30 | }, 31 | 32 | "rules": { 33 | 34 | // 35 | //Possible Errors 36 | // 37 | // The following rules point out areas where you might have made mistakes. 38 | // 39 | "comma-dangle": 2, // disallow or enforce trailing commas 40 | "no-cond-assign": 2, // disallow assignment in conditional expressions 41 | "no-console": 1, // disallow use of console (off by default in the node environment) 42 | "no-constant-condition": 2, // disallow use of constant expressions in conditions 43 | "no-control-regex": 2, // disallow control characters in regular expressions 44 | "no-debugger": 2, // disallow use of debugger 45 | "no-dupe-args": 2, // disallow duplicate arguments in functions 46 | "no-dupe-keys": 2, // disallow duplicate keys when creating object literals 47 | "no-duplicate-case": 2, // disallow a duplicate case label. 48 | "no-empty": 2, // disallow empty statements 49 | "no-empty-class": 2, // disallow the use of empty character classes in regular expressions 50 | "no-ex-assign": 2, // disallow assigning to the exception in a catch block 51 | "no-extra-boolean-cast": 2, // disallow double-negation boolean casts in a boolean context 52 | "no-extra-parens": 0, // disallow unnecessary parentheses (off by default) 53 | "no-extra-semi": 2, // disallow unnecessary semicolons 54 | "no-func-assign": 2, // disallow overwriting functions written as function declarations 55 | "no-inner-declarations": 2, // disallow function or variable declarations in nested blocks 56 | "no-invalid-regexp": 2, // disallow invalid regular expression strings in the RegExp constructor 57 | "no-irregular-whitespace": 2, // disallow irregular whitespace outside of strings and comments 58 | "no-negated-in-lhs": 2, // disallow negation of the left operand of an in expression 59 | "no-obj-calls": 2, // disallow the use of object properties of the global object (Math and JSON) as functions 60 | "no-regex-spaces": 2, // disallow multiple spaces in a regular expression literal 61 | "no-reserved-keys": 2, // disallow reserved words being used as object literal keys (off by default) 62 | "no-sparse-arrays": 2, // disallow sparse arrays 63 | "no-unreachable": 2, // disallow unreachable statements after a return, throw, continue, or break statement 64 | "use-isnan": 2, // disallow comparisons with the value NaN 65 | "valid-jsdoc": 2, // Ensure JSDoc comments are valid (off by default) 66 | "valid-typeof": 2, // Ensure that the results of typeof are compared against a valid string 67 | 68 | // 69 | // Best Practices 70 | // 71 | // These are rules designed to prevent you from making mistakes. 72 | // They either prescribe a better way of doing something or help you avoid footguns. 73 | // 74 | "block-scoped-var": 0, // treat var statements as if they were block scoped (off by default). 0: deep destructuring is not compatible https://github.com/eslint/eslint/issues/1863 75 | "complexity": 0, // specify the maximum cyclomatic complexity allowed in a program (off by default) 76 | "consistent-return": 2, // require return statements to either always or never specify values 77 | "curly": 2, // specify curly brace conventions for all control statements 78 | "default-case": 2, // require default case in switch statements (off by default) 79 | "dot-notation": 2, // encourages use of dot notation whenever possible 80 | "eqeqeq": 2, // require the use of === and !== 81 | "guard-for-in": 2, // make sure for-in loops have an if statement (off by default) 82 | "no-alert": 2, // disallow the use of alert, confirm, and prompt 83 | "no-caller": 2, // disallow use of arguments.caller or arguments.callee 84 | "no-div-regex": 2, // disallow division operators explicitly at beginning of regular expression (off by default) 85 | "no-else-return": 2, // disallow else after a return in an if (off by default) 86 | "no-empty-label": 2, // disallow use of labels for anything other then loops and switches 87 | "no-eq-null": 2, // disallow comparisons to null without a type-checking operator (off by default) 88 | "no-eval": 2, // disallow use of eval() 89 | "no-extend-native": 2, // disallow adding to native types 90 | "no-extra-bind": 2, // disallow unnecessary function binding 91 | "no-fallthrough": 2, // disallow fallthrough of case statements 92 | "no-floating-decimal": 2, // disallow the use of leading or trailing decimal points in numeric literals (off by default) 93 | "no-implied-eval": 2, // disallow use of eval()-like methods 94 | "no-iterator": 2, // disallow usage of __iterator__ property 95 | "no-labels": 2, // disallow use of labeled statements 96 | "no-lone-blocks": 2, // disallow unnecessary nested blocks 97 | "no-loop-func": 2, // disallow creation of functions within loops 98 | "no-multi-spaces": 2, // disallow use of multiple spaces 99 | "no-multi-str": 2, // disallow use of multiline strings 100 | "no-native-reassign": 2, // disallow reassignments of native objects 101 | "no-new": 2, // disallow use of new operator when not part of the assignment or comparison 102 | "no-new-func": 2, // disallow use of new operator for Function object 103 | "no-new-wrappers": 2, // disallows creating new instances of String,Number, and Boolean 104 | "no-octal": 2, // disallow use of octal literals 105 | "no-octal-escape": 2, // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251"; 106 | "no-param-reassign": 2, // disallow reassignment of function parameters (off by default) 107 | "no-proto": 2, // disallow usage of __proto__ property 108 | "no-redeclare": 2, // disallow declaring the same variable more then once 109 | "no-return-assign": 2, // disallow use of assignment in return statement 110 | "no-script-url": 2, // disallow use of javascript: urls. 111 | "no-self-compare": 2, // disallow comparisons where both sides are exactly the same (off by default) 112 | "no-sequences": 2, // disallow use of comma operator 113 | "no-throw-literal": 2, // restrict what can be thrown as an exception (off by default) 114 | "no-unused-expressions": 2, // disallow usage of expressions in statement position 115 | "no-void": 2, // disallow use of void operator (off by default) 116 | "no-warning-comments": [0, {"terms": ["todo", "fixme"], "location": "start"}], // disallow usage of configurable warning terms in comments": 2, // e.g. TODO or FIXME (off by default) 117 | "no-with": 2, // disallow use of the with statement 118 | "radix": 2, // require use of the second argument for parseInt() (off by default) 119 | "vars-on-top": 2, // requires to declare all vars on top of their containing scope (off by default) 120 | "wrap-iife": 2, // require immediate function invocation to be wrapped in parentheses (off by default) 121 | "yoda": 2, // require or disallow Yoda conditions 122 | 123 | // 124 | // Strict Mode 125 | // 126 | // These rules relate to using strict mode. 127 | // 128 | "strict": 0, // controls location of Use Strict Directives. 0: required by `babel-eslint` 129 | 130 | // 131 | // Variables 132 | // 133 | // These rules have to do with variable declarations. 134 | // 135 | "no-catch-shadow": 2, // disallow the catch clause parameter name being the same as a variable in the outer scope (off by default in the node environment) 136 | "no-delete-var": 2, // disallow deletion of variables 137 | "no-label-var": 2, // disallow labels that share a name with a variable 138 | "no-shadow": 2, // disallow declaration of variables already declared in the outer scope 139 | "no-shadow-restricted-names": 2, // disallow shadowing of names such as arguments 140 | "no-undef": 2, // disallow use of undeclared variables unless mentioned in a /*global */ block 141 | "no-undef-init": 2, // disallow use of undefined when initializing variables 142 | "no-undefined": 2, // disallow use of undefined variable (off by default) 143 | "no-unused-vars": 0, // disallow declaration of variables that are not used in the code 144 | "no-use-before-define": 2, // disallow use of variables before they are defined 145 | 146 | // 147 | //Stylistic Issues 148 | // 149 | // These rules are purely matters of style and are quite subjective. 150 | // 151 | "indent": [1, 2], // this option sets a specific tab width for your code (off by default) 152 | "brace-style": 1, // enforce one true brace style (off by default) 153 | "camelcase": 0, // require camel case names 154 | "comma-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after comma 155 | "comma-style": [1, "last"], // enforce one true comma style (off by default) 156 | "consistent-this": [1, "_this"], // enforces consistent naming when capturing the current execution context (off by default) 157 | "eol-last": 1, // enforce newline at the end of file, with no multiple empty lines 158 | "func-names": 0, // require function expressions to have a name (off by default) 159 | "func-style": 0, // enforces use of function declarations or expressions (off by default) 160 | "key-spacing": [1, {"beforeColon": false, "afterColon": true}], // enforces spacing between keys and values in object literal properties 161 | "max-nested-callbacks": [1, 3], // specify the maximum depth callbacks can be nested (off by default) 162 | "new-cap": [1, {newIsCap: true, capIsNew: false}], // require a capital letter for constructors 163 | "new-parens": 1, // disallow the omission of parentheses when invoking a constructor with no arguments 164 | "newline-after-var": 0, // allow/disallow an empty newline after var statement (off by default) 165 | "no-array-constructor": 1, // disallow use of the Array constructor 166 | "no-inline-comments": 1, // disallow comments inline after code (off by default) 167 | "no-lonely-if": 1, // disallow if as the only statement in an else block (off by default) 168 | "no-mixed-spaces-and-tabs": 1, // disallow mixed spaces and tabs for indentation 169 | "no-multiple-empty-lines": [1, {"max": 2}], // disallow multiple empty lines (off by default) 170 | "no-nested-ternary": 1, // disallow nested ternary expressions (off by default) 171 | "no-new-object": 1, // disallow use of the Object constructor 172 | "no-spaced-func": 1, // disallow space between function identifier and application 173 | "no-ternary": 0, // disallow the use of ternary operators (off by default) 174 | "no-trailing-spaces": 1, // disallow trailing whitespace at the end of lines 175 | "no-underscore-dangle": 0, // disallow dangling underscores in identifiers 176 | "no-wrap-func": 1, // disallow wrapping of non-IIFE statements in parens 177 | "one-var": [1, "never"], // allow just one var statement per function (off by default) 178 | "operator-assignment": [1, "never"], // require assignment operator shorthand where possible or prohibit it entirely (off by default) 179 | "padded-blocks": [1, "never"], // enforce padding within blocks (off by default) 180 | "quote-props": [1, "as-needed"], // require quotes around object literal property names (off by default) 181 | "quotes": [1, "single"], // specify whether double or single quotes should be used 182 | "semi": [1, "always"], // require or disallow use of semicolons instead of ASI 183 | "semi-spacing": [1, {"before": false, "after": true}], // enforce spacing before and after semicolons 184 | "sort-vars": 0, // sort variables within the same declaration block (off by default) 185 | "space-after-keywords": [1, "always"], // require a space after certain keywords (off by default) 186 | "space-before-blocks": [1, "always"], // require or disallow space before blocks (off by default) 187 | "space-before-function-paren": [1, {"anonymous": "always", "named": "never"}], // require or disallow space before function opening parenthesis (off by default) 188 | "space-in-brackets": [1, "never"], // require or disallow spaces inside brackets (off by default) 189 | "space-in-parens": [1, "never"], // require or disallow spaces inside parentheses (off by default) 190 | "space-infix-ops": [1, "always"], // require spaces around operators 191 | "space-return-throw-case": [1, "always"], // require a space after return, throw, and case 192 | "space-unary-ops": [1, {"words": true, "nonwords": false}], // Require or disallow spaces before/after unary operators (words on by default, nonwords off by default) 193 | "spaced-line-comment": [1, "always"], // require or disallow a space immediately following the // in a line comment (off by default) 194 | "wrap-regex": 0, // require regex literals to be wrapped in parentheses (off by default) 195 | 196 | // 197 | // ECMAScript 6 198 | // 199 | // These rules are only relevant to ES6 environments and are off by default. 200 | // 201 | "generator-star-spacing": [2, "before"], // enforce the spacing around the * in generator functions (off by default) 202 | 203 | // 204 | // Legacy 205 | // 206 | // The following rules are included for compatibility with JSHint and JSLint. 207 | // While the names of the rules may not match up with the JSHint/JSLint counterpart, 208 | // the functionality is the same. 209 | // 210 | "max-depth": [2, 3], // specify the maximum depth that blocks can be nested (off by default) 211 | "max-len": [2, 100, 2], // specify the maximum length of a line in your program (off by default) 212 | "max-params": [2, 5], // limits the number of parameters that can be used in the function declaration. (off by default) 213 | "max-statements": 0, // specify the maximum number of statement allowed in a function (off by default) 214 | "no-bitwise": 0, // disallow use of bitwise operators (off by default) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | demo/ 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2012 Netflix, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Falcor Wordpress Router 2 | 3 | [![npm version](https://badge.fury.io/js/falcor-wordpress.svg)](http://badge.fury.io/js/falcor-wordpress) 4 | 5 | This project provides a Falcor Router which acts as a middleman (in node.js) for consuming the [Wordpress REST API](https://github.com/WP-API/WP-API). The goal is a more intelligible, data-centric API for the client with dramatically reduced client network requests. 6 | 7 | ## Getting Started 8 | 9 | ### Basic installation and usage 10 | 11 | ``` 12 | npm install falcor-wordpress --save 13 | ``` 14 | 15 | Use it within an express app: 16 | 17 | ```javascript 18 | ... // basic express boilerplate, app = express(); 19 | 20 | var FalcorServer = require('falcor-express'); 21 | var wordpressRouter = require('falcor-wordpress'); 22 | var endpoint = 'http://demo.wp-api.org/wp-json'; 23 | 24 | app.use('/model.json', FalcorServer.dataSourceRoute(function (req, res) { 25 | // without authentication 26 | return wordpressRouter(endpoint); 27 | })); 28 | ``` 29 | 30 | ### Included Demo 31 | 32 | A demo running in express with a simple front-end is included. 33 | 34 | ``` 35 | # install library dependencies 36 | npm install 37 | 38 | # install demo dependencies and run 39 | cd demo 40 | npm install 41 | npm start 42 | 43 | # open your browser and visit http://localhost:9090 44 | ``` 45 | 46 | The demo page allows you to test live queries against the offical [Wordpress Rest API demo site](http://demo.wp-api.org/), including a number of examples to get you started. Watch the server console to see a list of REST endpoints that are being fetched on each flight. 47 | 48 | **Note:** there are some differences betweeen the 1.0 and 2.0 branches of the WP API. This package targets 2.0, and uses a 2.0 endpoint for its demo. 49 | 50 | ## Currently implemented top-level routes 51 | 52 | Below are the top-level routes available, along with some example paths that might be requested (try these live on the included demo). 53 | 54 | ### postsById 55 | 56 | Basic example: `postsById[171,131]["title","slug","link"]` 57 | 58 | ```json 59 | { 60 | "postsById": { 61 | "131": { 62 | "title": "Ipsam mollitia eveniet hic", 63 | "slug": "ipsam-mollitia-eveniet-hic", 64 | "link": "http://demo.wp-api.org/2015/08/21/ipsam-mollitia-eveniet-hic/" 65 | }, 66 | "171": { 67 | "title": "Sint aperiam autem molestiae debitis", 68 | "slug": "sint-aperiam-autem-molestiae-debitis", 69 | "link": "http://demo.wp-api.org/2015/08/12/sint-aperiam-autem-molestiae-debitis/" 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | Going deeper: `postsById[131].terms.categories[0].name` 76 | 77 | ```json 78 | { 79 | "postsById": { 80 | "131": { 81 | "terms": { 82 | "categories": { 83 | "0": { 84 | "name": "Illum in fugit assumenda quo et reprehenderit maxime saepe" 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | ``` 92 | 93 | ### recentPosts 94 | 95 | Basic example: `recentPosts[0..2].title` 96 | 97 | ```json 98 | { 99 | "recentPosts": { 100 | "0": { 101 | "title": "Repellat dolor architecto inventore" 102 | }, 103 | "1": { 104 | "title": "Dolor adipisci soluta eum ipsam deserunt" 105 | }, 106 | "2": { 107 | "title": "Ipsam mollitia eveniet hic" 108 | } 109 | } 110 | } 111 | ``` 112 | 113 | Going deeper: `recentPosts[5].terms.categories[0].name` 114 | 115 | ```json 116 | { 117 | "recentPosts": { 118 | "5": { 119 | "terms": { 120 | "categories": { 121 | "0": { 122 | "name": "In fugit quae libero a" 123 | } 124 | } 125 | } 126 | } 127 | } 128 | } 129 | ``` 130 | 131 | ## postsByTerm[vocabulary][term-slug] 132 | 133 | Example: `postsByTerm.categories.tool-kit[0..3]['title','id']` 134 | 135 | ```json 136 | { 137 | "postsByTerm": { 138 | "categories": { 139 | "tool-kit": { 140 | "0": { 141 | "title": "Writing Reports, Quick Guide", 142 | "id": 97 143 | }, 144 | "1": { 145 | "title": "Analyzing Data, Quick Guide", 146 | "id": 95 147 | }, 148 | "2": { 149 | "title": "Measuring Outcomes, Quick Guide", 150 | "id": 92 151 | }, 152 | "3": { 153 | "title": "Creating Learning Outcomes, Quick Guide", 154 | "id": 90 155 | } 156 | } 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | ## termsById[vocabulary] 163 | 164 | Categories example: `termsById.categories[4,70]['name','description']` 165 | 166 | ```json 167 | { 168 | "termsById": { 169 | "categories": { 170 | "4": { 171 | "name": "Ab iusto", 172 | "description": "Sunt distinctio asperiores dolores quae odit necessitatibus dolor dolore quo doloremque nam incidunt molestiae facilis quisquam voluptatem voluptas et voluptas sapiente laudantium fugiat" 173 | }, 174 | "70": { 175 | "name": "Accusantium nulla omnis quos", 176 | "description": "Occaecati placeat et dolores tempore unde est laudantium ipsam tempora accusamus culpa sequi aut aut dolore minus pariatur fugit ut ipsa et distinctio minus amet ut id molestiae assumenda aliquam vel qui quibusdam" 177 | } 178 | } 179 | } 180 | } 181 | ``` 182 | 183 | Tags example: `termsById.tags[36]['slug','name']` 184 | 185 | ```json 186 | { 187 | "termsById": { 188 | "tags": { 189 | "36": { 190 | "slug": "accusantium-dolore-porro-nihil-eveniet-dolores-impedit-quisquam", 191 | "name": "Accusantium dolore porro nihil eveniet dolores impedit quisquam" 192 | } 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | ## taxonomies 199 | 200 | Metadata: `taxonomies.category.meta['name','slug']` 201 | 202 | ```json 203 | { 204 | "taxonomies": { 205 | "category": { 206 | "meta": { 207 | "name": "Categories", 208 | "slug": "category" 209 | } 210 | } 211 | } 212 | } 213 | ``` 214 | 215 | Number of available terms: `taxonomies.categories.terms.length` 216 | 217 | ```json 218 | { 219 | "taxonomies": { 220 | "categories": { 221 | "terms": { 222 | "length": "52" 223 | } 224 | } 225 | } 226 | } 227 | ``` 228 | 229 | Terms range: `taxonomies.tags.terms[0..2]['name']` 230 | 231 | ```json 232 | { 233 | "taxonomies": { 234 | "tags": { 235 | "terms": { 236 | "0": { 237 | "name": "Accusantium dolore porro nihil eveniet dolores impedit quisquam" 238 | }, 239 | "1": { 240 | "name": "Ad et modi ipsam in iure" 241 | }, 242 | "2": { 243 | "name": "Adipisci ut tempora quisquam" 244 | } 245 | } 246 | } 247 | } 248 | } 249 | ``` 250 | 251 | ## Other routes 252 | 253 | These are also available at root, though generally more useful where referenced elsewhere: 254 | 255 | `authorsById` 256 | `mediaById` 257 | 258 | **Media** linked as featured image: `postsById[131].featured_media.media_details.sizes.thumbnail.file` 259 | 260 | ```json 261 | { 262 | "postsById": { 263 | "131": { 264 | "featured_media": { 265 | "media_details": { 266 | "sizes": { 267 | "thumbnail": { 268 | "file": "fba2be87-deae-3077-96b4-d1754a1802ca-150x150.jpg" 269 | } 270 | } 271 | } 272 | } 273 | } 274 | } 275 | } 276 | ``` 277 | 278 | **Author** of a post: `postsById[171].author.name` 279 | 280 | ```json 281 | { 282 | "postsById": { 283 | "171": { 284 | "author": { 285 | "name": "Cassidy" 286 | } 287 | } 288 | } 289 | } 290 | ``` 291 | 292 | 293 | 294 | 295 | 296 | ## Roadmap 297 | 298 | - [x] postsById 299 | - [x] recentPosts 300 | - [x] taxonomies (meta, terms list) 301 | - [x] termsById 302 | - [x] authorsById 303 | - [x] mediaById 304 | - [x] posts by term 305 | - [ ] more linked subqueries 306 | - [ ] additional listings by varied sorts 307 | -------------------------------------------------------------------------------- /authors.txt: -------------------------------------------------------------------------------- 1 | Jason Phillips 2 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 14 | 15 | 53 | 54 | 55 |
56 |

Wordpress Falcor Router Demo

57 | 58 |

Path Input

59 |

Try something like:

60 |
61 |
62 |
    63 |
  • postsById[171,131]["title","slug","link"]
  • 64 |
  • postsById[171,131].featured_media.guid
  • 65 |
  • postsById[131].terms.categories[0].name
  • 66 |
  • postsById[171].terms.tags.length
  • 67 |
    68 |
  • recentPosts[0..3].title
  • 69 |
  • recentPosts[5..8].terms.categories[0].name
  • 70 |
71 |
72 |
73 |
    74 |
  • termsById.tags[36]['slug','name']
  • 75 |
  • termsById.categories[4,70]['name','description']
  • 76 |
    77 |
  • taxonomies.category.meta['name','slug']
  • 78 |
  • taxonomies.categories.terms.length
  • 79 |
  • taxonomies.tags.terms[0..5]['name','slug']
  • 80 |
    81 |
  • authorsById[41].name
  • 82 |
83 |
84 |
85 | 86 |
87 |
88 | 89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 |

Response

98 |

 99 |       
100 |
101 |

Full Cache

102 |

103 |       
104 |
105 | 106 |
107 | 108 | 109 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "falcor-wordpress-example", 3 | "version": "0.0.5", 4 | "description": "A an example falcor server for using the Falcor Wordpress router.", 5 | "author": { 6 | "name": "Jason Phillips", 7 | "url": "https://github.com/jasonphillips" 8 | }, 9 | "licenses": [ 10 | { 11 | "type": "Apache License, Version 2.0", 12 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 13 | } 14 | ], 15 | "scripts": { 16 | "start": "babel-node server.js | bunyan" 17 | }, 18 | "dependencies": { 19 | "body-parser": "^1.12.4", 20 | "bunyan": "^1.4.0", 21 | "express": "~4.11.1", 22 | "falcor-express": "https://github.com/jasonphillips/falcor-express.git#jsonHeader" 23 | }, 24 | "devDependencies": { 25 | "babel": "^5.8.23" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This file starts the server and exposes the Router at /model.json 4 | var express = require('express'); 5 | var bodyParser = require('body-parser'); 6 | var app = express(); 7 | var FalcorServer = require('falcor-express'); 8 | var wordpressRouter = require('../lib/wordpressRouter.js'); 9 | 10 | var bunyan = require('bunyan'); 11 | var log = bunyan.createLogger({name: 'express'}); 12 | var endpoint = 'http://demo.wp-api.org/wp-json'; 13 | 14 | app.use(bodyParser.urlencoded({extended: false})); 15 | 16 | // Simple middleware to handle get/post 17 | app.use('/model.json', FalcorServer.dataSourceRoute(function (req, res) { 18 | // Not using authentication in demo 19 | return wordpressRouter(endpoint); 20 | })); 21 | 22 | app.use(express.static('.')); 23 | 24 | let server = app.listen(9090, function (err) { 25 | if (err) { 26 | log.error(err); 27 | return; 28 | } 29 | log.info('navigate to http://localhost:9090'); 30 | }); 31 | -------------------------------------------------------------------------------- /lib/handlers/authorsById.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchById = require('./fetchById.js'); 16 | 17 | // handler is "authors" rather than "users" because 18 | // it does not request via authentication, and only 19 | // users with posts (authors) are retrievable by 20 | // anonymous request in the WP API 2.0 21 | 22 | var AuthorsById = (function (_FetchById) { 23 | _inherits(AuthorsById, _FetchById); 24 | 25 | function AuthorsById() { 26 | _classCallCheck(this, AuthorsById); 27 | 28 | _get(Object.getPrototypeOf(AuthorsById.prototype), 'constructor', this).apply(this, arguments); 29 | } 30 | 31 | _createClass(AuthorsById, [{ 32 | key: 'getIdQuery', 33 | value: function getIdQuery(id) { 34 | return this.wp.users().id(id); 35 | } 36 | }, { 37 | key: 'getReturnPath', 38 | value: function getReturnPath(id, key) { 39 | return typeof key !== 'undefined' ? ['authorsById', id, key] : ['authorsById', id]; 40 | } 41 | }]); 42 | 43 | return AuthorsById; 44 | })(FetchById); 45 | 46 | exports['default'] = AuthorsById; 47 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/fetchById.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 10 | 11 | var _ = require('lodash'); 12 | var jsonGraph = require('falcor-json-graph'); 13 | var $ref = jsonGraph.ref; 14 | var $error = jsonGraph.error; 15 | 16 | var FetchById = (function () { 17 | function FetchById(shared, ids, keys) { 18 | _classCallCheck(this, FetchById); 19 | 20 | this.wp = shared.wp; 21 | this.log = shared.log; 22 | this.cacheGet = shared.cacheGet.bind(shared); 23 | this.ids = ids; 24 | this.keys = keys; 25 | } 26 | 27 | // Override with wp api promise 28 | 29 | _createClass(FetchById, [{ 30 | key: 'getIdQuery', 31 | value: function getIdQuery(id) { 32 | return null; 33 | } 34 | 35 | // Override with specific Falcor return paths 36 | }, { 37 | key: 'getReturnPath', 38 | value: function getReturnPath(id, key) { 39 | // keyless for error handler 40 | return typeof key !== 'undefined' ? [id, key] : [id]; 41 | } 42 | 43 | // Override with cache path to check for already retrieved object 44 | }, { 45 | key: 'getCachedPath', 46 | value: function getCachedPath(id) { 47 | return '' + id; 48 | } 49 | 50 | // Override with keys that should return references {key=>path} 51 | }, { 52 | key: 'getReferenceKeys', 53 | value: function getReferenceKeys() { 54 | return {}; 55 | } 56 | }, { 57 | key: 'getPromises', 58 | value: function getPromises() { 59 | var _this = this; 60 | 61 | var promises = _.map(this.ids, function (id) { 62 | var itemPromise = new Promise(function (resolve, reject) { 63 | // check cache for already fetched item this flight 64 | var cached = _this.cacheGet(_this.getCachedPath(id)); 65 | if (typeof cached !== 'undefined') { 66 | resolve(cached); 67 | } else { 68 | var query = _this.getIdQuery(id); 69 | _this.log.info('GET: ' + query._renderURI()); 70 | 71 | query.then(function (response) { 72 | resolve(response); 73 | })['catch'](function (err) { 74 | _this.log.error(err); 75 | resolve({ error: err.status + ' for ' + err.path }); 76 | }); 77 | } 78 | }); 79 | 80 | return itemPromise; 81 | }); 82 | 83 | return promises; 84 | } 85 | }, { 86 | key: 'resolvePromises', 87 | value: function resolvePromises(promises) { 88 | var _this2 = this; 89 | 90 | return Promise.all(promises).then(function (records) { 91 | var results = []; 92 | var referenceKeys = _this2.getReferenceKeys(); 93 | 94 | _this2.ids.forEach(function (id, offset) { 95 | var record = records[offset]; 96 | 97 | _this2.keys.forEach(function (key) { 98 | if (record.error) { 99 | results.push({ 100 | path: _this2.getReturnPath(id, key), 101 | value: $error(record.error) 102 | }); 103 | } else if (record.id || record.name) { 104 | var value = record[key]; 105 | // reference keys 106 | if (typeof referenceKeys[key] === 'function') { 107 | results.push({ 108 | path: _this2.getReturnPath(id, key), 109 | value: $ref(referenceKeys[key](id, value)) 110 | }); 111 | } else { 112 | if (typeof value === 'object' && value.rendered) { 113 | // handle WP 2.0 {rendered} values 114 | value = value.rendered; 115 | } 116 | results.push({ 117 | path: _this2.getReturnPath(id, key), 118 | value: value 119 | }); 120 | } 121 | } else { 122 | results.push({ 123 | path: _this2.getReturnPath(id), 124 | value: $error(id + ' not found') 125 | }); 126 | } 127 | }); 128 | }); 129 | return results; 130 | }); 131 | } 132 | }, { 133 | key: 'buildReturn', 134 | value: function buildReturn() { 135 | return this.resolvePromises(this.getPromises()); 136 | } 137 | }]); 138 | 139 | return FetchById; 140 | })(); 141 | 142 | exports['default'] = FetchById; 143 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/fetchByIndices.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 8 | 9 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | var _ = require('lodash'); 14 | var jsonGraph = require('falcor-json-graph'); 15 | var $ref = jsonGraph.ref; 16 | var $error = jsonGraph.error; 17 | 18 | var FetchByIndices = (function () { 19 | function FetchByIndices(shared, indices, filter) { 20 | _classCallCheck(this, FetchByIndices); 21 | 22 | this.wp = shared.wp; 23 | this.log = shared.log; 24 | this.indices = indices; 25 | this.cacheSet = shared.cacheSet.bind(shared); 26 | this.filter = filter; 27 | this.paging = this.getPaging(); 28 | } 29 | 30 | // Override with wp api promise 31 | 32 | _createClass(FetchByIndices, [{ 33 | key: 'getRootQuery', 34 | value: function getRootQuery() { 35 | return null; 36 | } 37 | 38 | // responses will be references to a byId path 39 | }, { 40 | key: 'getReferencePath', 41 | value: function getReferencePath(record) { 42 | return record.id; 43 | } 44 | 45 | // Override with specific Falcor return paths 46 | }, { 47 | key: 'getReturnPath', 48 | value: function getReturnPath(index) { 49 | return typeof index !== 'undefined' ? [index] : null; 50 | } 51 | }, { 52 | key: 'getPromise', 53 | value: function getPromise() { 54 | var _this = this; 55 | 56 | var rangePromise = new Promise(function (resolve, reject) { 57 | var query = _this.getRootQuery(); 58 | if (_this.filter) { 59 | query = query.filter(_this.filter); 60 | } 61 | if (_this.paging) { 62 | query = query.perPage(_this.paging.perPage).page(_this.paging.page); 63 | } 64 | _this.log.info('GET: ' + query._renderURI()); 65 | 66 | query.then(function (response) { 67 | resolve(response); 68 | })['catch'](function (err) { 69 | _this.log.error(err); 70 | resolve({ error: err.status + ' for ' + err.path }); 71 | }); 72 | }); 73 | return rangePromise; 74 | } 75 | }, { 76 | key: 'resolvePromise', 77 | value: function resolvePromise(rangePromise) { 78 | var _this2 = this; 79 | 80 | return rangePromise.then(function (records) { 81 | var results = []; 82 | var offset = _this2.paging ? _this2.paging.offset : 0; 83 | var total = _this2.paging && records._paging ? records._paging.total : records.length; 84 | var referencesList = []; 85 | 86 | // build item references and cache objects 87 | _.forEach(records, function (record) { 88 | var refPath = _this2.getReferencePath(record); 89 | referencesList.push($ref(refPath)); 90 | _this2.cacheSet(refPath, record); 91 | }); 92 | 93 | _this2.indices.forEach(function (index) { 94 | if (index === 'length') { 95 | results.push({ 96 | path: _this2.getReturnPath(index), 97 | value: total 98 | }); 99 | } else { 100 | results.push({ 101 | path: _this2.getReturnPath(index), 102 | value: referencesList[index - offset] 103 | }); 104 | } 105 | }); 106 | 107 | return results; 108 | })['catch'](function (err) { 109 | _this2.log.error(err); 110 | return [{ 111 | path: _this2.getReturnPath(), 112 | value: $error(err.status + ' for ' + err.path) 113 | }]; 114 | }); 115 | } 116 | }, { 117 | key: 'buildReturn', 118 | value: function buildReturn() { 119 | return this.resolvePromise(this.getPromise()); 120 | } 121 | }, { 122 | key: 'getPaging', 123 | value: function getPaging() { 124 | var perPage;var page; 125 | var indices = _.without(this.indices, 'length'); 126 | if (!indices.length) { 127 | indices = [0]; 128 | } 129 | 130 | var _calculatePage = this.calculatePage(_.min(indices), _.max(indices)); 131 | 132 | var _calculatePage2 = _slicedToArray(_calculatePage, 2); 133 | 134 | perPage = _calculatePage2[0]; 135 | page = _calculatePage2[1]; 136 | 137 | return { 138 | perPage: perPage, 139 | page: page, 140 | offset: perPage * (page - 1) 141 | }; 142 | } 143 | 144 | // Calculate smallest page for range 145 | }, { 146 | key: 'calculatePage', 147 | value: function calculatePage(low, high) { 148 | var range = high - low + 1; 149 | if (range < low) { 150 | var perpage = undefined; 151 | for (perpage = range; perpage <= low; perpage++) { 152 | if (low % perpage <= perpage - range) { 153 | var offset = Math.floor(low / perpage); 154 | return [perpage, offset + 1]; 155 | } 156 | } 157 | return perpage; 158 | } 159 | return [high + 1, 1]; 160 | } 161 | }]); 162 | 163 | return FetchByIndices; 164 | })(); 165 | 166 | exports['default'] = FetchByIndices; 167 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/mediaById.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchById = require('./fetchById.js'); 16 | 17 | var MediaById = (function (_FetchById) { 18 | _inherits(MediaById, _FetchById); 19 | 20 | function MediaById() { 21 | _classCallCheck(this, MediaById); 22 | 23 | _get(Object.getPrototypeOf(MediaById.prototype), 'constructor', this).apply(this, arguments); 24 | } 25 | 26 | _createClass(MediaById, [{ 27 | key: 'getIdQuery', 28 | value: function getIdQuery(id) { 29 | return this.wp.media().id(id); 30 | } 31 | }, { 32 | key: 'getReturnPath', 33 | value: function getReturnPath(id, key) { 34 | return typeof key !== 'undefined' ? ['mediaById', id, key] : ['mediaById', id]; 35 | } 36 | }]); 37 | 38 | return MediaById; 39 | })(FetchById); 40 | 41 | exports['default'] = MediaById; 42 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/postsById.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchById = require('./fetchById.js'); 16 | 17 | var PostsById = (function (_FetchById) { 18 | _inherits(PostsById, _FetchById); 19 | 20 | function PostsById() { 21 | _classCallCheck(this, PostsById); 22 | 23 | _get(Object.getPrototypeOf(PostsById.prototype), 'constructor', this).apply(this, arguments); 24 | } 25 | 26 | _createClass(PostsById, [{ 27 | key: 'getIdQuery', 28 | value: function getIdQuery(id) { 29 | return this.wp.posts().id(id); 30 | } 31 | }, { 32 | key: 'getReturnPath', 33 | value: function getReturnPath(id, key) { 34 | return typeof key !== 'undefined' ? ['postsById', id, key] : ['postsById', id]; 35 | } 36 | }, { 37 | key: 'getCachedPath', 38 | value: function getCachedPath(id) { 39 | return 'postsById[' + id + ']'; 40 | } 41 | }, { 42 | key: 'getReferenceKeys', 43 | value: function getReferenceKeys() { 44 | return { 45 | author: function author(id, value) { 46 | return 'authorsById[' + value + ']'; 47 | }, 48 | featured_media: function featured_media(id, value) { 49 | return 'mediaById[' + value + ']'; 50 | }, 51 | terms: function terms(id, value) { 52 | return 'termsByPost[' + id + ']'; 53 | } 54 | }; 55 | } 56 | }]); 57 | 58 | return PostsById; 59 | })(FetchById); 60 | 61 | exports['default'] = PostsById; 62 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/postsByIndices.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchByIndices = require('./FetchByIndices.js'); 16 | 17 | var PostsByIndices = (function (_FetchByIndices) { 18 | _inherits(PostsByIndices, _FetchByIndices); 19 | 20 | function PostsByIndices() { 21 | _classCallCheck(this, PostsByIndices); 22 | 23 | _get(Object.getPrototypeOf(PostsByIndices.prototype), 'constructor', this).apply(this, arguments); 24 | } 25 | 26 | _createClass(PostsByIndices, [{ 27 | key: 'getRootQuery', 28 | value: function getRootQuery() { 29 | return this.wp.posts(); 30 | } 31 | }, { 32 | key: 'getReferencePath', 33 | value: function getReferencePath(record) { 34 | return 'postsById[' + record.id + ']'; 35 | } 36 | }, { 37 | key: 'getReturnPath', 38 | value: function getReturnPath(index) { 39 | return typeof index !== 'undefined' ? ['recentPosts', index] : ['recentPosts']; 40 | } 41 | }]); 42 | 43 | return PostsByIndices; 44 | })(FetchByIndices); 45 | 46 | exports['default'] = PostsByIndices; 47 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/postsByTerm.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchByIndices = require('./FetchByIndices.js'); 16 | 17 | var PostsByTerm = (function (_FetchByIndices) { 18 | _inherits(PostsByTerm, _FetchByIndices); 19 | 20 | function PostsByTerm(shared, indices, filter, vocabulary, term) { 21 | _classCallCheck(this, PostsByTerm); 22 | 23 | _get(Object.getPrototypeOf(PostsByTerm.prototype), 'constructor', this).call(this, shared, indices, filter); 24 | this.vocabulary = vocabulary; 25 | this.term = term; 26 | } 27 | 28 | _createClass(PostsByTerm, [{ 29 | key: 'getRootQuery', 30 | value: function getRootQuery() { 31 | return this.wp.posts().taxonomy(this.vocabulary, this.term); 32 | } 33 | }, { 34 | key: 'getReferencePath', 35 | value: function getReferencePath(record) { 36 | return 'postsById[' + record.id + ']'; 37 | } 38 | }, { 39 | key: 'getReturnPath', 40 | value: function getReturnPath(index) { 41 | return typeof index !== 'undefined' ? ['postsByTerm', this.vocabulary, this.term, index] : ['postsByTerm', this.vocabulary, this.term]; 42 | } 43 | }]); 44 | 45 | return PostsByTerm; 46 | })(FetchByIndices); 47 | 48 | exports['default'] = PostsByTerm; 49 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/taxonomiesById.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchById = require('./fetchById.js'); 16 | 17 | var TaxonomiesById = (function (_FetchById) { 18 | _inherits(TaxonomiesById, _FetchById); 19 | 20 | function TaxonomiesById() { 21 | _classCallCheck(this, TaxonomiesById); 22 | 23 | _get(Object.getPrototypeOf(TaxonomiesById.prototype), 'constructor', this).apply(this, arguments); 24 | } 25 | 26 | _createClass(TaxonomiesById, [{ 27 | key: 'getIdQuery', 28 | value: function getIdQuery(id) { 29 | return this.wp.taxonomies().taxonomy(id); 30 | } 31 | }, { 32 | key: 'getReturnPath', 33 | value: function getReturnPath(id, key) { 34 | return typeof key !== 'undefined' ? ['taxonomies', id, 'meta', key] : ['taxonomies', id, 'meta']; 35 | } 36 | }]); 37 | 38 | return TaxonomiesById; 39 | })(FetchById); 40 | 41 | exports['default'] = TaxonomiesById; 42 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/termsById.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchById = require('./fetchById.js'); 16 | 17 | var TermsById = (function (_FetchById) { 18 | _inherits(TermsById, _FetchById); 19 | 20 | function TermsById(shared, ids, keys, vocabulary) { 21 | _classCallCheck(this, TermsById); 22 | 23 | _get(Object.getPrototypeOf(TermsById.prototype), 'constructor', this).call(this, shared, ids, keys); 24 | this.vocabulary = vocabulary; 25 | } 26 | 27 | _createClass(TermsById, [{ 28 | key: 'getIdQuery', 29 | value: function getIdQuery(id) { 30 | return this.wp[this.vocabulary]().id(id); 31 | } 32 | }, { 33 | key: 'getReturnPath', 34 | value: function getReturnPath(id, key) { 35 | return typeof key !== 'undefined' ? ['termsById', this.vocabulary, id, key] : ['termsById', this.vocabulary, id]; 36 | } 37 | }, { 38 | key: 'getCachedPath', 39 | value: function getCachedPath(id) { 40 | return 'termsById.' + this.vocabulary + '[' + id + ']'; 41 | } 42 | }, { 43 | key: 'getReferenceKeys', 44 | value: function getReferenceKeys() { 45 | var _this = this; 46 | 47 | return { 48 | children: function children(id, value) { 49 | return 'taxonomies.' + _this.vocabulary + '.termsByParentId[' + id + ']'; 50 | } 51 | }; 52 | } 53 | }]); 54 | 55 | return TermsById; 56 | })(FetchById); 57 | 58 | exports['default'] = TermsById; 59 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/termsByIndices.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchByIndices = require('./FetchByIndices.js'); 16 | 17 | var TermsByIndices = (function (_FetchByIndices) { 18 | _inherits(TermsByIndices, _FetchByIndices); 19 | 20 | function TermsByIndices(shared, indices, filter, vocabulary, parentId) { 21 | _classCallCheck(this, TermsByIndices); 22 | 23 | _get(Object.getPrototypeOf(TermsByIndices.prototype), 'constructor', this).call(this, shared, indices, filter); 24 | this.vocabulary = vocabulary; 25 | this.parentId = parentId; 26 | } 27 | 28 | _createClass(TermsByIndices, [{ 29 | key: 'getRootQuery', 30 | value: function getRootQuery() { 31 | var query = this.wp[this.vocabulary](); 32 | if (this.parentId) { 33 | query = query.parent(this.parentId); 34 | } 35 | return query; 36 | } 37 | }, { 38 | key: 'getReferencePath', 39 | value: function getReferencePath(record) { 40 | return 'termsById.' + this.vocabulary + '[' + record.id + ']'; 41 | } 42 | }, { 43 | key: 'getReturnPath', 44 | value: function getReturnPath(index) { 45 | var base = ['taxonomies', this.vocabulary, this.parentId ? 'termsByParentId' : 'terms']; 46 | if (this.parentId) { 47 | base.push(this.parentId); 48 | } 49 | return typeof index !== 'undefined' ? base.concat([index]) : base; 50 | } 51 | }]); 52 | 53 | return TermsByIndices; 54 | })(FetchByIndices); 55 | 56 | exports['default'] = TermsByIndices; 57 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/handlers/termsByPost.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 8 | 9 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 10 | 11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 12 | 13 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 14 | 15 | var FetchByIndices = require('./FetchByIndices.js'); 16 | 17 | var TermsByPost = (function (_FetchByIndices) { 18 | _inherits(TermsByPost, _FetchByIndices); 19 | 20 | function TermsByPost(shared, indices, filter, postId, vocabulary) { 21 | _classCallCheck(this, TermsByPost); 22 | 23 | _get(Object.getPrototypeOf(TermsByPost.prototype), 'constructor', this).call(this, shared, indices, filter); 24 | this.postId = postId; 25 | this.vocabulary = vocabulary; 26 | } 27 | 28 | _createClass(TermsByPost, [{ 29 | key: 'getRootQuery', 30 | value: function getRootQuery() { 31 | return this.wp.taxonomies().collection(this.vocabulary).forPost(this.postId); 32 | } 33 | 34 | // pagination does not exist on post terms 35 | }, { 36 | key: 'getPaging', 37 | value: function getPaging() { 38 | return false; 39 | } 40 | }, { 41 | key: 'getReferencePath', 42 | value: function getReferencePath(record) { 43 | return 'termsById.' + this.vocabulary + '[' + record.id + ']'; 44 | } 45 | }, { 46 | key: 'getReturnPath', 47 | value: function getReturnPath(index) { 48 | return typeof index !== 'undefined' ? ['termsByPost', this.postId, this.vocabulary, index] : ['termsByPost', this.postId, this.vocabulary]; 49 | } 50 | }]); 51 | 52 | return TermsByPost; 53 | })(FetchByIndices); 54 | 55 | exports['default'] = TermsByPost; 56 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/wordpressRouter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 4 | 5 | var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 6 | 7 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 8 | 9 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 10 | 11 | var Router = require('falcor-router'); 12 | var Promise = require('promise'); 13 | var _ = require('lodash'); 14 | 15 | // falcor basics 16 | var jsonGraph = require('falcor-json-graph'); 17 | var $ref = jsonGraph.ref; 18 | var $error = jsonGraph.error; 19 | 20 | // wordpress client for API 2.0 21 | var WP = require('wordpress-rest-api'); 22 | 23 | // handlers 24 | var PostsById = require('./handlers/postsById.js'); 25 | var PostsByIndices = require('./handlers/postsByIndices.js'); 26 | var PostsByTerm = require('./handlers/postsByTerm.js'); 27 | var TermsById = require('./handlers/termsById.js'); 28 | var TaxonomiesById = require('./handlers/taxonomiesById.js'); 29 | var TermsByIndices = require('./handlers/termsByIndices.js'); 30 | var TermsByPost = require('./handlers/termsByPost.js'); 31 | var AuthorsById = require('./handlers/authorsById.js'); 32 | var MediaById = require('./handlers/mediaById.js'); 33 | 34 | // logging 35 | var bunyan = require('bunyan'); 36 | var log = bunyan.createLogger({ name: 'wordpressRouter' }); 37 | log.level(process.env.NODE_ENV === 'production' ? 'fatal' : 'info'); 38 | 39 | /* 40 | * Extend Falcor Router 41 | */ 42 | 43 | var WordpressRouter = (function (_Router$createClass) { 44 | _inherits(WordpressRouter, _Router$createClass); 45 | 46 | function WordpressRouter(endpoint, auth) { 47 | _classCallCheck(this, WordpressRouter); 48 | 49 | _get(Object.getPrototypeOf(WordpressRouter.prototype), 'constructor', this).call(this); 50 | var options = { endpoint: endpoint }; 51 | // auth object should have username, password fields 52 | if (typeof auth !== 'undefined') { 53 | _.merge(options, auth, { auth: true }); 54 | } 55 | this.wp = new WP(options); 56 | this.log = log; 57 | // caching for rendundant data in a single flight 58 | this.cache = {}; 59 | } 60 | 61 | /* 62 | * Export generator for disposable instances 63 | */ 64 | 65 | _createClass(WordpressRouter, [{ 66 | key: 'cacheSet', 67 | value: function cacheSet(identifier, value) { 68 | this.cache[identifier] = value; 69 | } 70 | }, { 71 | key: 'cacheGet', 72 | value: function cacheGet(identifier) { 73 | return this.cache[identifier]; 74 | } 75 | }]); 76 | 77 | return WordpressRouter; 78 | })(Router.createClass([{ 79 | route: 'postsById[{integers:postIds}][{keys:props}]', 80 | get: function get(pathSet) { 81 | var handler = new PostsById(this, pathSet.postIds, pathSet.props); 82 | return handler.buildReturn(); 83 | } 84 | }, { 85 | route: 'postsByTerm[{keys:vocabularies}][{keys:terms}][{keys:indices}]', 86 | get: function get(pathSet) { 87 | var _this = this; 88 | 89 | var promises = _.map(pathSet.vocabularies, function (vocabulary) { 90 | return _.map(pathSet.terms, function (term) { 91 | var handler = new PostsByTerm(_this, pathSet.indices, { orderby: 'date' }, vocabulary, term); 92 | return handler.buildReturn(); 93 | }); 94 | }); 95 | return Promise.all(_.flatten(promises)).then(function (records) { 96 | return _.flatten(records); 97 | }); 98 | } 99 | }, { 100 | route: 'termsByPost[{integers:postIds}][{keys:vocabularies}][{keys:indices}]', 101 | get: function get(pathSet) { 102 | var _this2 = this; 103 | 104 | var promises = []; 105 | _.forEach(pathSet.postIds, function (postId) { 106 | _.forEach(pathSet.vocabularies, function (vocabulary) { 107 | var handler = new TermsByPost(_this2, pathSet.indices, {}, postId, vocabulary); 108 | promises.push(handler.buildReturn()); 109 | }); 110 | }); 111 | return Promise.all(promises).then(function (records) { 112 | return _.flatten(records); 113 | }); 114 | } 115 | }, { 116 | route: 'taxonomies[{keys:vocabularies}].termsByParentId[{integers:parentIds}][{keys:indices}]', 117 | get: function get(pathSet) { 118 | var _this3 = this; 119 | 120 | var promises = []; 121 | _.forEach(pathSet.vocabularies, function (vocabulary) { 122 | _.forEach(pathSet.parentIds, function (parentId) { 123 | var handler = new TermsByIndices(_this3, pathSet.indices, { orderby: 'date' }, vocabulary, parentId); 124 | promises.push(handler.buildReturn()); 125 | }); 126 | }); 127 | return Promise.all(promises).then(function (records) { 128 | return _.flatten(records); 129 | }); 130 | } 131 | }, { 132 | route: 'recentPosts[{keys:indices}]', 133 | get: function get(pathSet) { 134 | var handler = new PostsByIndices(this, pathSet.indices, { orderby: 'date' }); 135 | return handler.buildReturn(); 136 | } 137 | }, { 138 | route: 'taxonomies[{keys:vocabularies}].terms[{keys:indices}]', 139 | get: function get(pathSet) { 140 | var _this4 = this; 141 | 142 | var promises = _.map(pathSet.vocabularies, function (vocabulary) { 143 | var handler = new TermsByIndices(_this4, pathSet.indices, null, vocabulary); 144 | return handler.buildReturn(); 145 | }); 146 | return Promise.all(promises).then(function (records) { 147 | return _.flatten(records); 148 | }); 149 | } 150 | }, { 151 | route: 'taxonomies[{keys:vocabularies}].meta[{keys:props}]', 152 | get: function get(pathSet) { 153 | var handler = new TaxonomiesById(this, pathSet.vocabularies, pathSet.props); 154 | return handler.buildReturn(); 155 | } 156 | }, { 157 | route: 'termsById[{keys:vocabularies}][{integers:categoryIds}][{keys:props}]', 158 | get: function get(pathSet) { 159 | var _this5 = this; 160 | 161 | var promises = _.map(pathSet.vocabularies, function (vocabulary) { 162 | var handler = new TermsById(_this5, pathSet.categoryIds, pathSet.props, vocabulary); 163 | return handler.buildReturn(); 164 | }); 165 | return Promise.all(promises).then(function (records) { 166 | return _.flatten(records); 167 | }); 168 | } 169 | }, { 170 | route: 'authorsById[{integers:userIds}][{keys:props}]', 171 | get: function get(pathSet) { 172 | var handler = new AuthorsById(this, pathSet.userIds, pathSet.props); 173 | return handler.buildReturn(); 174 | } 175 | }, { 176 | route: 'mediaById[{integers:mediaIds}][{keys:props}]', 177 | get: function get(pathSet) { 178 | var handler = new MediaById(this, pathSet.mediaIds, pathSet.props); 179 | return handler.buildReturn(); 180 | } 181 | }])); 182 | 183 | module.exports = function (endpoint, auth) { 184 | return new WordpressRouter(endpoint, auth); 185 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "falcor-wordpress", 3 | "version": "0.3.0", 4 | "description": "A falcor server for consuming the wordpress API.", 5 | "homepage": "https://github.com/jasonphillips/falcor-wordpress", 6 | "main": "./lib/wordpressRouter.js", 7 | "author": { 8 | "name": "Jason Phillips", 9 | "url": "https://github.com/jasonphillips" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/jasonphillips/falcor-wordpress.git" 14 | }, 15 | "licenses": [ 16 | { 17 | "type": "Apache License, Version 2.0", 18 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html" 19 | } 20 | ], 21 | "scripts": { 22 | "lint": "eslint src/*.js src/handlers/*.js", 23 | "compile": "babel -d lib/ src/", 24 | "test": "_mocha tests --recursive", 25 | "prepublish": "npm run compile" 26 | }, 27 | "dependencies": { 28 | "bunyan": "^1.4.0", 29 | "falcor-json-graph": "~1.1.5", 30 | "falcor-router": "~0.2.9", 31 | "lodash": "^3.10.1", 32 | "promise": "^7.0.4", 33 | "wordpress-rest-api": "https://github.com/kadamwhite/wordpress-rest-api#20160614-dynamic-route-generation-refactor-merge" 34 | }, 35 | "devDependencies": { 36 | "babel": "^5.8.23", 37 | "babel-eslint": "^4.1.1", 38 | "chai": "^3.5.0", 39 | "eslint": "^0.21.0", 40 | "falcor": "^0.1.17", 41 | "nock": "^8.0.0", 42 | "sinon": "^1.17.4", 43 | "sinon-chai": "^2.8.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/handlers/authorsById.js: -------------------------------------------------------------------------------- 1 | var FetchById = require('./fetchById.js'); 2 | 3 | // handler is "authors" rather than "users" because 4 | // it does not request via authentication, and only 5 | // users with posts (authors) are retrievable by 6 | // anonymous request in the WP API 2.0 7 | 8 | class AuthorsById extends FetchById { 9 | 10 | getIdQuery(id) { 11 | return this.wp.users().id(id); 12 | } 13 | 14 | getReturnPath(id, key) { 15 | return (typeof key !== 'undefined') ? ['authorsById', id, key] : ['authorsById', id]; 16 | } 17 | } 18 | 19 | export default AuthorsById; 20 | -------------------------------------------------------------------------------- /src/handlers/fetchById.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var jsonGraph = require('falcor-json-graph'); 3 | var $ref = jsonGraph.ref; 4 | var $error = jsonGraph.error; 5 | 6 | class FetchById { 7 | 8 | constructor(shared, ids, keys) { 9 | this.wp = shared.wp; 10 | this.log = shared.log; 11 | this.cacheGet = shared.cacheGet.bind(shared); 12 | this.ids = ids; 13 | this.keys = keys; 14 | } 15 | 16 | // Override with wp api promise 17 | getIdQuery(id) { 18 | return null; 19 | } 20 | 21 | // Override with specific Falcor return paths 22 | getReturnPath(id, key) { 23 | // keyless for error handler 24 | return (typeof key !== 'undefined') ? [id, key] : [id]; 25 | } 26 | 27 | // Override with cache path to check for already retrieved object 28 | getCachedPath(id) { 29 | return `${id}`; 30 | } 31 | 32 | // Override with keys that should return references {key=>path} 33 | getReferenceKeys() { 34 | return {}; 35 | } 36 | 37 | getPromises() { 38 | var promises = _.map(this.ids, (id) => { 39 | var itemPromise = new Promise((resolve, reject) => { 40 | // check cache for already fetched item this flight 41 | let cached = this.cacheGet(this.getCachedPath(id)); 42 | if (typeof cached !== 'undefined') { 43 | resolve(cached); 44 | } else { 45 | let query = this.getIdQuery(id); 46 | this.log.info('GET: ' + query._renderURI()); 47 | 48 | query.then((response) => { 49 | resolve(response); 50 | }).catch((err) => { 51 | this.log.error(err); 52 | resolve({error: `${err.status} for ${err.path}`}); 53 | }); 54 | } 55 | }); 56 | 57 | return itemPromise; 58 | }); 59 | 60 | return promises; 61 | } 62 | 63 | resolvePromises(promises) { 64 | return Promise.all(promises).then((records) => { 65 | var results = []; 66 | var referenceKeys = this.getReferenceKeys(); 67 | 68 | this.ids.forEach((id, offset) => { 69 | var record = records[offset]; 70 | 71 | this.keys.forEach((key) => { 72 | if (record.error) { 73 | results.push({ 74 | path: this.getReturnPath(id, key), 75 | value: $error(record.error) 76 | }); 77 | } else if (record.id || record.name) { 78 | let value = record[key]; 79 | // reference keys 80 | if (typeof referenceKeys[key] === 'function') { 81 | results.push({ 82 | path: this.getReturnPath(id, key), 83 | value: $ref(referenceKeys[key](id, value)) 84 | }); 85 | } else { 86 | if (typeof value === 'object' && value.rendered) { 87 | // handle WP 2.0 {rendered} values 88 | value = value.rendered; 89 | } 90 | results.push({ 91 | path: this.getReturnPath(id, key), 92 | value: value 93 | }); 94 | } 95 | } else { 96 | results.push({ 97 | path: this.getReturnPath(id), 98 | value: $error(id + ' not found') 99 | }); 100 | } 101 | }); 102 | }); 103 | return results; 104 | }); 105 | } 106 | 107 | buildReturn() { 108 | return this.resolvePromises(this.getPromises()); 109 | } 110 | } 111 | 112 | export default FetchById; 113 | -------------------------------------------------------------------------------- /src/handlers/fetchByIndices.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var jsonGraph = require('falcor-json-graph'); 3 | var $ref = jsonGraph.ref; 4 | var $error = jsonGraph.error; 5 | 6 | class FetchByIndices { 7 | 8 | constructor(shared, indices, filter) { 9 | this.wp = shared.wp; 10 | this.log = shared.log; 11 | this.indices = indices; 12 | this.cacheSet = shared.cacheSet.bind(shared); 13 | this.filter = filter; 14 | this.paging = this.getPaging(); 15 | } 16 | 17 | // Override with wp api promise 18 | getRootQuery() { 19 | return null; 20 | } 21 | 22 | // responses will be references to a byId path 23 | getReferencePath(record) { 24 | return record.id; 25 | } 26 | 27 | // Override with specific Falcor return paths 28 | getReturnPath(index) { 29 | return (typeof index !== 'undefined') ? [index] : null; 30 | } 31 | 32 | getPromise() { 33 | var rangePromise = new Promise((resolve, reject) => { 34 | var query = this.getRootQuery(); 35 | if (this.filter) { 36 | query = query.filter(this.filter); 37 | } 38 | if (this.paging) { 39 | query = query.perPage(this.paging.perPage) 40 | .page(this.paging.page); 41 | } 42 | this.log.info('GET: ' + query._renderURI()); 43 | 44 | query 45 | .then((response) => { 46 | resolve(response); 47 | }).catch((err) => { 48 | this.log.error(err); 49 | resolve({error: `${err.status} for ${err.path}`}); 50 | }); 51 | }); 52 | return rangePromise; 53 | } 54 | 55 | resolvePromise(rangePromise) { 56 | return rangePromise.then((records) => { 57 | var results = []; 58 | var offset = this.paging ? this.paging.offset : 0; 59 | var total = (this.paging && records._paging) ? records._paging.total : records.length; 60 | var referencesList = []; 61 | 62 | // build item references and cache objects 63 | _.forEach(records, (record) => { 64 | let refPath = this.getReferencePath(record); 65 | referencesList.push($ref(refPath)); 66 | this.cacheSet(refPath, record); 67 | }); 68 | 69 | this.indices.forEach((index) => { 70 | if (index === 'length') { 71 | results.push({ 72 | path: this.getReturnPath(index), 73 | value: total 74 | }); 75 | } else { 76 | results.push({ 77 | path: this.getReturnPath(index), 78 | value: referencesList[index - offset] 79 | }); 80 | } 81 | }); 82 | 83 | return results; 84 | }).catch((err) => { 85 | this.log.error(err); 86 | return [ 87 | { 88 | path: this.getReturnPath(), 89 | value: $error(`${err.status} for ${err.path}`) 90 | } 91 | ]; 92 | }); 93 | } 94 | 95 | buildReturn() { 96 | return this.resolvePromise(this.getPromise()); 97 | } 98 | 99 | getPaging() { 100 | var perPage; var page; 101 | var indices = _.without(this.indices, 'length'); 102 | if (!indices.length) { 103 | indices = [0]; 104 | } 105 | [perPage, page] = this.calculatePage(_.min(indices), _.max(indices)); 106 | return { 107 | perPage: perPage, 108 | page: page, 109 | offset: perPage * (page - 1) 110 | }; 111 | } 112 | 113 | // Calculate smallest page for range 114 | calculatePage(low, high) { 115 | var range = high - low + 1; 116 | if (range < low) { 117 | let perpage; 118 | for (perpage = range; perpage <= low; perpage++) { 119 | if (low % perpage <= perpage - range) { 120 | let offset = Math.floor(low / perpage); 121 | return [perpage, offset + 1]; 122 | } 123 | } 124 | return perpage; 125 | } 126 | return [high + 1, 1]; 127 | } 128 | } 129 | 130 | export default FetchByIndices; 131 | -------------------------------------------------------------------------------- /src/handlers/mediaById.js: -------------------------------------------------------------------------------- 1 | var FetchById = require('./fetchById.js'); 2 | 3 | class MediaById extends FetchById { 4 | 5 | getIdQuery(id) { 6 | return this.wp.media().id(id); 7 | } 8 | 9 | getReturnPath(id, key) { 10 | return (typeof key !== 'undefined') ? ['mediaById', id, key] : ['mediaById', id]; 11 | } 12 | } 13 | 14 | export default MediaById; 15 | -------------------------------------------------------------------------------- /src/handlers/postsById.js: -------------------------------------------------------------------------------- 1 | var FetchById = require('./fetchById.js'); 2 | 3 | class PostsById extends FetchById { 4 | 5 | getIdQuery(id) { 6 | return this.wp.posts().id(id); 7 | } 8 | 9 | getReturnPath(id, key) { 10 | return (typeof key !== 'undefined') ? ['postsById', id, key] : ['postsById', id]; 11 | } 12 | 13 | getCachedPath(id) { 14 | return `postsById[${id}]`; 15 | } 16 | 17 | getReferenceKeys() { 18 | return { 19 | author: (id, value) => `authorsById[${value}]`, 20 | featured_media: (id, value) => `mediaById[${value}]`, 21 | terms: (id, value) => `termsByPost[${id}]` 22 | }; 23 | } 24 | } 25 | 26 | export default PostsById; 27 | -------------------------------------------------------------------------------- /src/handlers/postsByIndices.js: -------------------------------------------------------------------------------- 1 | var FetchByIndices = require('./FetchByIndices.js'); 2 | 3 | class PostsByIndices extends FetchByIndices { 4 | 5 | getRootQuery() { 6 | return this.wp.posts(); 7 | } 8 | 9 | getReferencePath(record) { 10 | return `postsById[${record.id}]`; 11 | } 12 | 13 | getReturnPath(index) { 14 | return (typeof index !== 'undefined') ? ['recentPosts', index] : ['recentPosts']; 15 | } 16 | } 17 | 18 | export default PostsByIndices; 19 | -------------------------------------------------------------------------------- /src/handlers/postsByTerm.js: -------------------------------------------------------------------------------- 1 | var FetchByIndices = require('./FetchByIndices.js'); 2 | 3 | class PostsByTerm extends FetchByIndices { 4 | 5 | constructor(shared, indices, filter, vocabulary, term) { 6 | super(shared, indices, filter); 7 | this.vocabulary = vocabulary; 8 | this.term = term; 9 | } 10 | 11 | getRootQuery() { 12 | return this.wp.posts().taxonomy(this.vocabulary, this.term); 13 | } 14 | 15 | getReferencePath(record) { 16 | return `postsById[${record.id}]`; 17 | } 18 | 19 | getReturnPath(index) { 20 | return (typeof index !== 'undefined') 21 | ? ['postsByTerm', this.vocabulary, this.term, index] 22 | : ['postsByTerm', this.vocabulary, this.term]; 23 | } 24 | } 25 | 26 | export default PostsByTerm; 27 | -------------------------------------------------------------------------------- /src/handlers/taxonomiesById.js: -------------------------------------------------------------------------------- 1 | var FetchById = require('./fetchById.js'); 2 | 3 | class TaxonomiesById extends FetchById { 4 | 5 | getIdQuery(id) { 6 | return this.wp.taxonomies().taxonomy(id); 7 | } 8 | 9 | getReturnPath(id, key) { 10 | return (typeof key !== 'undefined') 11 | ? ['taxonomies', id, 'meta', key] 12 | : ['taxonomies', id, 'meta']; 13 | } 14 | } 15 | 16 | export default TaxonomiesById; 17 | -------------------------------------------------------------------------------- /src/handlers/termsById.js: -------------------------------------------------------------------------------- 1 | var FetchById = require('./fetchById.js'); 2 | 3 | class TermsById extends FetchById { 4 | 5 | constructor(shared, ids, keys, vocabulary) { 6 | super(shared, ids, keys); 7 | this.vocabulary = vocabulary; 8 | } 9 | 10 | getIdQuery(id) { 11 | return this.wp[this.vocabulary]().id(id); 12 | } 13 | 14 | getReturnPath(id, key) { 15 | return (typeof key !== 'undefined') 16 | ? ['termsById', this.vocabulary, id, key] 17 | : ['termsById', this.vocabulary, id]; 18 | } 19 | 20 | getCachedPath(id) { 21 | return `termsById.${this.vocabulary}[${id}]`; 22 | } 23 | 24 | getReferenceKeys() { 25 | return { 26 | children: (id, value) => `taxonomies.${this.vocabulary}.termsByParentId[${id}]` 27 | }; 28 | } 29 | } 30 | 31 | export default TermsById; 32 | -------------------------------------------------------------------------------- /src/handlers/termsByIndices.js: -------------------------------------------------------------------------------- 1 | var FetchByIndices = require('./FetchByIndices.js'); 2 | 3 | class TermsByIndices extends FetchByIndices { 4 | 5 | constructor(shared, indices, filter, vocabulary, parentId) { 6 | super(shared, indices, filter); 7 | this.vocabulary = vocabulary; 8 | this.parentId = parentId; 9 | } 10 | 11 | getRootQuery() { 12 | let query = this.wp[this.vocabulary](); 13 | if (this.parentId) { 14 | query = query.parent(this.parentId); 15 | } 16 | return query; 17 | } 18 | 19 | getReferencePath(record) { 20 | return `termsById.${this.vocabulary}[${record.id}]`; 21 | } 22 | 23 | getReturnPath(index) { 24 | let base = ['taxonomies', this.vocabulary, this.parentId ? 'termsByParentId' : 'terms']; 25 | if (this.parentId) { 26 | base.push(this.parentId); 27 | } 28 | return (typeof index !== 'undefined') 29 | ? base.concat([index]) 30 | : base; 31 | } 32 | } 33 | 34 | export default TermsByIndices; 35 | -------------------------------------------------------------------------------- /src/handlers/termsByPost.js: -------------------------------------------------------------------------------- 1 | var FetchByIndices = require('./FetchByIndices.js'); 2 | 3 | class TermsByPost extends FetchByIndices { 4 | 5 | constructor(shared, indices, filter, postId, vocabulary) { 6 | super(shared, indices, filter); 7 | this.postId = postId; 8 | this.vocabulary = vocabulary; 9 | } 10 | 11 | getRootQuery() { 12 | return this.wp.taxonomies().collection(this.vocabulary).forPost(this.postId); 13 | } 14 | 15 | // pagination does not exist on post terms 16 | getPaging() { 17 | return false; 18 | } 19 | 20 | getReferencePath(record) { 21 | return `termsById.${this.vocabulary}[${record.id}]`; 22 | } 23 | 24 | getReturnPath(index) { 25 | return (typeof index !== 'undefined') 26 | ? ['termsByPost', this.postId, this.vocabulary, index] 27 | : ['termsByPost', this.postId, this.vocabulary]; 28 | } 29 | } 30 | 31 | export default TermsByPost; 32 | -------------------------------------------------------------------------------- /src/wordpressRouter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Router = require('falcor-router'); 4 | var Promise = require('promise'); 5 | var _ = require('lodash'); 6 | 7 | // falcor basics 8 | var jsonGraph = require('falcor-json-graph'); 9 | var $ref = jsonGraph.ref; 10 | var $error = jsonGraph.error; 11 | 12 | // wordpress client for API 2.0 13 | var WP = require('wordpress-rest-api'); 14 | 15 | // handlers 16 | var PostsById = require('./handlers/postsById.js'); 17 | var PostsByIndices = require('./handlers/postsByIndices.js'); 18 | var PostsByTerm = require('./handlers/postsByTerm.js'); 19 | var TermsById = require('./handlers/termsById.js'); 20 | var TaxonomiesById = require('./handlers/taxonomiesById.js'); 21 | var TermsByIndices = require('./handlers/termsByIndices.js'); 22 | var TermsByPost = require('./handlers/termsByPost.js'); 23 | var AuthorsById = require('./handlers/authorsById.js'); 24 | var MediaById = require('./handlers/mediaById.js'); 25 | 26 | // logging 27 | var bunyan = require('bunyan'); 28 | var log = bunyan.createLogger({name: 'wordpressRouter'}); 29 | log.level(process.env.NODE_ENV === 'production' ? 'fatal' : 'info'); 30 | 31 | /* 32 | * Extend Falcor Router 33 | */ 34 | class WordpressRouter extends 35 | Router.createClass([ 36 | { 37 | route: 'postsById[{integers:postIds}][{keys:props}]', 38 | get: function (pathSet) { 39 | var handler = new PostsById(this, pathSet.postIds, pathSet.props); 40 | return handler.buildReturn(); 41 | } 42 | }, 43 | 44 | { 45 | route: 'postsByTerm[{keys:vocabularies}][{keys:terms}][{keys:indices}]', 46 | get: function (pathSet) { 47 | var promises = _.map(pathSet.vocabularies, (vocabulary) => { 48 | return _.map(pathSet.terms, (term) => { 49 | var handler = new PostsByTerm( 50 | this, pathSet.indices, {orderby: 'date'}, vocabulary, term 51 | ); 52 | return handler.buildReturn(); 53 | }); 54 | }); 55 | return Promise.all(_.flatten(promises)).then((records) => { 56 | return _.flatten(records); 57 | }); 58 | } 59 | }, 60 | 61 | { 62 | route: 'termsByPost[{integers:postIds}][{keys:vocabularies}][{keys:indices}]', 63 | get: function (pathSet) { 64 | var promises = []; 65 | _.forEach(pathSet.postIds, (postId) => { 66 | _.forEach(pathSet.vocabularies, (vocabulary) => { 67 | var handler = new TermsByPost( 68 | this, pathSet.indices, {}, postId, vocabulary 69 | ); 70 | promises.push(handler.buildReturn()); 71 | }); 72 | }); 73 | return Promise.all(promises).then((records) => { 74 | return _.flatten(records); 75 | }); 76 | } 77 | }, 78 | 79 | { 80 | route: 'taxonomies[{keys:vocabularies}].termsByParentId[{integers:parentIds}][{keys:indices}]', 81 | get: function (pathSet) { 82 | var promises = []; 83 | _.forEach(pathSet.vocabularies, (vocabulary) => { 84 | _.forEach(pathSet.parentIds, (parentId) => { 85 | var handler = new TermsByIndices( 86 | this, pathSet.indices, {orderby: 'date'}, vocabulary, parentId 87 | ); 88 | promises.push(handler.buildReturn()); 89 | }); 90 | }); 91 | return Promise.all(promises).then((records) => { 92 | return _.flatten(records); 93 | }); 94 | } 95 | }, 96 | 97 | { 98 | route: 'recentPosts[{keys:indices}]', 99 | get: function (pathSet) { 100 | var handler = new PostsByIndices(this, pathSet.indices, {orderby: 'date'}); 101 | return handler.buildReturn(); 102 | } 103 | }, 104 | 105 | { 106 | route: 'taxonomies[{keys:vocabularies}].terms[{keys:indices}]', 107 | get: function (pathSet) { 108 | var promises = _.map(pathSet.vocabularies, (vocabulary) => { 109 | var handler = new TermsByIndices( 110 | this, pathSet.indices, null, vocabulary 111 | ); 112 | return handler.buildReturn(); 113 | }); 114 | return Promise.all(promises).then((records) => { 115 | return _.flatten(records); 116 | }); 117 | } 118 | }, 119 | 120 | { 121 | route: 'taxonomies[{keys:vocabularies}].meta[{keys:props}]', 122 | get: function (pathSet) { 123 | var handler = new TaxonomiesById(this, pathSet.vocabularies, pathSet.props); 124 | return handler.buildReturn(); 125 | } 126 | }, 127 | 128 | { 129 | route: 'termsById[{keys:vocabularies}][{integers:categoryIds}][{keys:props}]', 130 | get: function (pathSet) { 131 | var promises = _.map(pathSet.vocabularies, (vocabulary) => { 132 | var handler = new TermsById( 133 | this, pathSet.categoryIds, pathSet.props, vocabulary 134 | ); 135 | return handler.buildReturn(); 136 | }); 137 | return Promise.all(promises).then((records) => { 138 | return _.flatten(records); 139 | }); 140 | } 141 | }, 142 | 143 | { 144 | route: 'authorsById[{integers:userIds}][{keys:props}]', 145 | get: function (pathSet) { 146 | var handler = new AuthorsById(this, pathSet.userIds, pathSet.props); 147 | return handler.buildReturn(); 148 | } 149 | }, 150 | 151 | { 152 | route: 'mediaById[{integers:mediaIds}][{keys:props}]', 153 | get: function (pathSet) { 154 | var handler = new MediaById(this, pathSet.mediaIds, pathSet.props); 155 | return handler.buildReturn(); 156 | } 157 | } 158 | 159 | ]) { 160 | constructor(endpoint, auth) { 161 | super(); 162 | let options = {endpoint: endpoint}; 163 | // auth object should have username, password fields 164 | if (typeof auth !== 'undefined') { 165 | _.merge(options, auth, {auth: true}); 166 | } 167 | this.wp = new WP(options); 168 | this.log = log; 169 | // caching for rendundant data in a single flight 170 | this.cache = {}; 171 | } 172 | 173 | cacheSet(identifier, value) { 174 | this.cache[identifier] = value; 175 | } 176 | 177 | cacheGet(identifier) { 178 | return this.cache[identifier]; 179 | } 180 | } 181 | 182 | /* 183 | * Export generator for disposable instances 184 | */ 185 | 186 | module.exports = function (endpoint, auth) { 187 | return new WordpressRouter(endpoint, auth); 188 | }; 189 | -------------------------------------------------------------------------------- /tests/fixtures/responses/postsByIdMultiple: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://demo.wp-api.org:443", 4 | "method": "GET", 5 | "path": "/wp-json/wp/v2/posts/1", 6 | "body": "", 7 | "status": 200, 8 | "response": [ 9 | "1f8b0800000000000003ad55d16adb3014fd1555cf49ec94650393a60c36d8de0a2bf4612a41b1af1dadb224a4eba6c1e4df77afdb642dddf29005f2a0d8f239e79e7baed44b53c9623a9295469085bccca71fc7f96c3ccd6ff34fc5e5acf83093cf2f974d8bffdcd0740cd3cb08ae8208b4966bc4900a95a9ac82d64f3661ac8399f8d8a8ec3a5c4de56e245b5f99da0cbbffcebbdf70943bd9ae613eb0d68f373eda8a14e3367039c127a47fd6b887e392985f65f94c65d35c65afb054c668062dc1bda9ef1bf38981ef828b29bd437064d19b5df3b0b8035bfa16047a31d7621da1be52afdc796d8c928bbb1bf1f9e6bbf8429e891f0621cd55a6171371bb3649d06febbb286a13130a2e6e22be5606858fa2020b08c2e048e01a9c48a8238a95f54d635c73413061a11c2b85a7126238aef49d8cb32ad01dae7d1c725783c68e12b36ca1325a16393bd9b6e4e4922ac02e51df7c00475d0854c7bb87094df9b09545ad6d8291ac7d6c35e7943e76958e9c8592a2ddf86880b07e4eefa99dbae125ad969c0c5af73281ade9592fb941c7b3421dfb95bc53d926a8ecf1928ca546244a8edc1164e9ad85128d77ff053760e995efa898d354f10890aa610458d8de744283760555a5571c6a8c1df97642d55d8278a83a42b0cf069f09fd2504e99af5f37141153c1221f93aa651401fa9e9a719b36f97ca223c1a464c03fc26141a51976b4edfa9e0438caf83a693702f9b7011623b20a27ef2ceb7a47d1fcb2d25f42cfdf813f38367a39eb27e20e4ba9794fd7311f2181da838f9ddcb8cf5d2e9968fdf4d20ae77d1e27b60f37c13f411ec8ef620b4c1d298d2c5c179dcddef76bf018bd61a649a060000" 10 | ], 11 | "headers": { 12 | "content-type": "application/json; charset=UTF-8", 13 | "content-length": "620", 14 | "connection": "close", 15 | "access-control-allow-headers": "Authorization", 16 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 17 | "allow": "GET", 18 | "content-encoding": "gzip", 19 | "date": "Sat, 11 Jun 2016 20:48:49 GMT", 20 | "link": "; rel=\"alternate\"; type=text/html", 21 | "server": "nginx/1.4.6 (Ubuntu)", 22 | "x-batcache": "MISS", 23 | "x-cached": "MISS", 24 | "x-content-type-options": "nosniff", 25 | "x-ec2-instance-id": "i-f737987d", 26 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 27 | "vary": "Cookie", 28 | "x-cache": "Miss from cloudfront", 29 | "via": "1.1 3e4642ebc640ace400836140916c7de0.cloudfront.net (CloudFront)", 30 | "x-amz-cf-id": "X1rNg2i6HChBi1LKClOv8eRuW6EAW9UszzBTXkRBN_7R8pvj9BAL9Q==" 31 | } 32 | }, 33 | { 34 | "scope": "https://demo.wp-api.org:443", 35 | "method": "GET", 36 | "path": "/wp-json/wp/v2/posts/100", 37 | "body": "", 38 | "status": 200, 39 | "response": [ 40 | "1f8b0800000000000003c55adb721cb711fd15641ff2a499d5356531325d2a492e2915cba6652b95f2ba54e00cc8453437cd85a4a3d2bfe79c6e0083a5442751549507d3e4ee0cd0e83e7dfa7443ef37bede1cddb97dfbd6a6b6b3db1c6deedebef3a7e2f683e2f6c39feede3bba77fbe8fec38d7ef9e6bc9d6f7ce07ce142ef37a3eb6a373afcbed9cff3301dedb6bb6deddabebc1c0a3bf8b21fcf775b6eb2dbde7e80ff1eeeb6ee6a70b59f6d61a76969b1007e1babbd9f5d35f745dd37fdb8db6e3edcdab47dedcfbc2cfe6933e303bf6bead42ce730ef3fd815079f7f1be895a19f66fcd5f8eeed973f1976f173836d0edcf72c78c524af98d52b46bc429f547d37bb0e813978f9d1fecef1b3d92cb3b9e89b659811dbd65cb8c6cf06e76e7c654f7b83cf867eb4c676bef58f765bbcb3eb1e2d0d7f36fef864f16672b5f19d71d3ac3b2eadc186937bb72cddbc8c78090fe2f1dd565fd36ddd6c7c6d180cf38e8b58373833e247d3d8d90c637fe16bd85c9ad762119eeb75d96a7658d5f865745c01ebc0e6d27c7f76e62bef273c348e388d874d1758a0c763e97ca6eb3b83e5e3074bfbf1919ef4ad58450387c9b666015ad321709467fd64ec4267612b38077bcab2f410bd909e8d073e6dfaeaedbba59fddf1a3e1f83550ece8753b0d6eec2c0fa35eaf96f6dde24af374a97cbb4ca6410046df8b0df04aab67fea1b195c3196c0b0bab65f04c8bd99947d60844bede6d9e2ccd60cde4bb19beed8d43442618d7782c8fdfd463bb8dd98fee0c8f330925072f2f2fcbe776a9de9655dfeeb6962fd8b6c01a859bf13f3f21fd60b48753277e2416ba827ee2d7e57e6e9bdde67845e3305a37210a7e694bf39d3def3c4f2eafc12805179d6b8fcd637c7170063ac3b871ecc7f4c2e846bc267801bc10e2659a7bfe6dcb1b8ef337b7efdc58faeeac07cb6c8e797a9ccb022774926ecd0897e6a5dffb0668e6c299eb473bfbbec3f72f8838fd1a6e110c986e4160f02a60d50b002d6239322040c5d9726eb1d252dbe080678008305e2d930581adb8c499f6be42d6b9b19277b15f4b8c95e2157c4457c40d32279de0e421694089800c7c2811efa79b1cf2aadadbf6d48de7f08a441961f44dd36b78134c0a5fc76876ae7213fc86339d2e937aacf158a2d77c2fcdb3abca0dc0b1574c0b3c989b3485deb4a7a5f9d1213d6124fc42b8203b2b5f8320d67c8535927848032c399bbeedf07048d5c93c8ec48f25e1bbae3fc5b76ddf20e33cfe16688006e425f13c284f98e200838f6b3ff8a9f26a3a0f6442804d6baf7c7b90472f045e2d608be42372d6ed3ac00421212b8debc96a807dc4a16e72fe5f6cd59f4e7d17f0e88087a2f615ca5ae0d902640a947664024b72426244522c0349d6811e022c2297013138b6073344d7023da94406d0833222a34b75709264883f7dae0e779639e65248991c1a328412559a68a7eb1d6850d6102766a07c857a8024e86f72c2535fbd75dda4551e6b8249e266d96f423ebec62719e7c03591247f9ea5f0b0f8700d52b0ba8b4e2d951d910db18213a407d9c5d271f8492a60ea94da35d016cb540abb23d2586cad1c7268807986b3c10f008fa0be343fa0fcf4cabdceaa69825718096e4885162c4fcda25b615d595fcf66780279130124db027c28102170d7bdfd5cc3c5572e800877cdeb2aae48ecdf554fddd8f6f3ac694f54fb4208b408e59aa5a840cef982a5a38071c5dccfb685d74f5171906448e81a470c1528e24cfd1e61998aa28d78245bd2ff52f4a470269ad1928e07b0353982d093bab52a10e45d9917866f51fe40c4312b02236749189d2c9e8a7f289f9a2c62be154569a2ddee9359fbed685b1fd3f5eac053adef96495de5e0c7c2d6aa43511a134f440e5dcd5067d98a458045fe31335289ca5de5fc4938b2bab45203e0793055287f7662858a75c5740e84462a0dac9c844f69feaa3c0df081e96aac859450ed2220db133cf4fbb0d45e6b124c87020456236a27fc82674e10444a35143e6104682b0253513ca53360fd50d5f398bde8089d6bd0cc244773d18fa44529481d7434cc2a261ca9487fb8e05d7c81d314d03405e96e2e064523402a07439d463563aa8a400a94980b52984225ab9203b80092d2e9439ca6120f0cc7f891693748d8fd3d51bc310e907b083cfccf5a906f8117f1285f18e5e783e327aa8845edd1893e3e2f9f4c0b2238509a2479059a8d6155870a9c833b847c293ab3af848b19272f11d328419e828d42a500979c2175a406536f505f287d30e96190d65fc9bf3551789607eb5992f40f2a155fabbcc7cf970004ad4a1410732b9658783ea0387fed1998952507ffc9214280d2b2aa1b598333d0476247a4d35a517023727f289084a32b8ae347a7a3d96d7900df9ea30cce50bc2f5451a5353425a83e88d169ac72552c00dbf74d5dfa79b77d70fffed5fd7be84a01386c3cac9ee9635384f38830000671602b3997c40bf32e19cc6e02694e76cf088c5eca1f614115cd24049f74a0f80af83d787cb75533d064bdd6620309251d141ea325cc2a66870aeb608d767519d6d1a7fc90a145ba3f1287c152e74490f622dc3a543298415427fac8d381720018a426d6a7563a0c6f23f499e45ecb030c3db3153a176c13ce333b680f7838c86d9810a84d5c34e1d0b3b81ca766d1d1b101df081649c2a5457f9e0d5bb0955b95f90ef333f1281c106581919ca153350e49a9d23e1b54260cc894ae9a0c27488689da8aeded6903f605ede2cbd81c05e682e1f2f8a10a8fef4d1897a0bd5281262d0300a14944433b2c194587f4239087d253e0a983000586800049c709f25bbc987d7c4d34a54a073722856d02406902e5016fe870958872ba0a2a167a55ec056f6b520221e8744371c65c21ba97e813b9120f84c5a4748d6e0f4412614ac7095c7074e25569f90e3a8167742c5e41fee92c81ed5da8a06a2d7df554541a7328223828bd90555264a40b8c81fb4869a6f2cc1324e39232d5d14aec5cb4b4d0a8685c3cc30d256938be266c3325fe789d30e4e05fdbf364cd8dede273c89fa6d1c22cb615481d1411e89eb9d0cea85021247192ba1c569dc090796d053804c9aadc1032686bd01ceab0f83f65478fe14d6a2d4f986011c9b142292480924e75495e06919148b96966431f0b286b524caba0641d5bead2c074df5aec708a4c01afc0ef528a10791633511846c59af4f038419ca3acc8c7531604532a966418268dacc63240195062a397ab222010d98d56bfc74c8a122267b068522ca13cd74d02ea95c57863acb5a3eaabcaba0a64cb014dd85c85298453cfee32e84df53da202dd89d5d1dab1eb0e431fb1a534a0477ca593baa00b8162407c36a21daff750e8aab4da28a1c802c870ae8157622aafaecbc0fa23f6c7daf829fe044ad6c4be76f0b5a9f9a9bfc468e6d4ff1313e2b910ca2a284f01cf10a72238b2909cc6e99957ca43883f8c5a8d911cd1f1a2fa267e2573158bcee74711e03890d68cb449a430b44c79804fa89c0f07536afa738b16189d980cba75ee50409115c1f968bde2b0a490d6622d0f85b4e79a72b1b9d0524e1111b470184be92142876c72f5a7c3134934518c0c10742c5560a2042d1061da4aea62e94663bd2a19264becdc53d36a31e6d301109e77107ea16b898c2ee30ef334a674ecdf85f5638e46f09c643db3c4e01a5c52a3939a40195d4c9fece4fe8eb6a244a30585c41991b617d2df16b18a022b5a3311800c7dd27044162f04a3c091b419f823a4d155200b69d424a7a901516c41303a23174d08f4e92014ec9211219cade25b91094662dd612e898440eca60190100915a604fa443eee87f11c58b056f55a2d207a935e7fc5093b8c4db2fafba0a0b4d88107545e6aa8fbda886ff2171eb302a7a1ba4e42658c903f24553fa08654aa24c845c55569f7572ad4628a09f9e1f14fa8f9d04349279534b6402376435c3d90b049f4971bc53457a697d8e75fbd8e2319d1c9697696c6c3c9e0388a09dc21770fc800ee7dd02ca517be8f449ce420e99e8d1765164b616ec753111a31fd0ef4bf9c15cc2a751669884980942f0ec0f225c8a08732514664e94284eca4041d593e7ffb49bccc88070427e0dee7306ab1b7666495e857be40d9e56037c76a5a3fb5257779e5849b32502790aa5a22965978322fb5d16e2cb2bfcb362b4199a519d685f1082779ace6693344e05b6d1a740670cd4d308035200c1865a10c7578f967f29af60d69cdd85a260062a4112a1d87a739bba677b0d473449bea23e349140fce4ff3a75eb1275336b007a038e1ed4d9c84932ab5e812ebf9fb2fb143acdb2c035a9d549de5cf0148a106c7592fc786ab1c50e88658f1e290668fc3471787d09cffd5bd21871202c5dfb9266429c651097310a544e5ff752788ce45c998f6a42b40f3efeefdcc97bad9fbe58f7bdc82fae1cfbf22781c30301448c47d3fe21efed6e60cc358342bf59b1657e27673f4f02b5ef1b6b8159fdf4c68b39709f7cffde03adc460fbe3b5f3fac9a1e28c2c7985b576f7fdb1c9dd9667258b11f5bcb8b7bbc8e9ba9918f4042ba73dc3f3aacf6cb9d5f71016dcff92b7e7bc33b6efcfe7e33b9e60c9fbddff02af1f76fbd71b7ff0fccf876dbcb61b7bdb88b93a1204fbb2dfe61c1e60316ad307844fdc25ddbffb4a0ac858bd305c7f93cbb789d0fbbe43a9f8645cf6335879bb3bab6a7bc88c78c129efb8c732fb823e2b9c552647da32efe42ab07204cdfd0feafa3732109267816221317a62302ff79ae5943b6db8eee027a11634d39c7e57014611950f9a5dc25cbedb60fbf8afbd879b6d59e68ffdc63c892df0c16b3d0d54538c18c0b125973b6577dd7b7f0534c83df90115f24fa6b5a6511baf51ed995b6a497df20dbbed4964cdc6c33e61aee6a25afdf6f30b6e13f5eb91cb0db4760e63fc6b9d42ee5fde89a0f7886ea00ff4283ffc08619f0e1d70f1ffe05c7185ce321240000" 41 | ], 42 | "headers": { 43 | "content-type": "application/json; charset=UTF-8", 44 | "content-length": "3560", 45 | "connection": "close", 46 | "access-control-allow-headers": "Authorization", 47 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 48 | "allow": "GET", 49 | "content-encoding": "gzip", 50 | "date": "Sat, 11 Jun 2016 20:48:49 GMT", 51 | "link": "; rel=\"alternate\"; type=text/html", 52 | "server": "nginx/1.4.6 (Ubuntu)", 53 | "x-batcache": "MISS", 54 | "x-cached": "MISS", 55 | "x-content-type-options": "nosniff", 56 | "x-ec2-instance-id": "i-f737987d", 57 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 58 | "vary": "Cookie", 59 | "x-cache": "Miss from cloudfront", 60 | "via": "1.1 c381fe6202a5a2a18615c3efcbdca28a.cloudfront.net (CloudFront)", 61 | "x-amz-cf-id": "RDj2GV5WOtH2XtVILixPwTKEdNjyd9B6cMhSwPKn9Ke5m3f1nNArtw==" 62 | } 63 | } 64 | ] -------------------------------------------------------------------------------- /tests/fixtures/responses/postsByIdSingle: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://demo.wp-api.org:443", 4 | "method": "GET", 5 | "path": "/wp-json/wp/v2/posts/1", 6 | "body": "", 7 | "status": 200, 8 | "response": [ 9 | "1f8b0800000000000003ad55d16adb3014fd1555cf49ec94650393a60c36d8de0a2bf4612a41b1af1dadb224a4eba6c1e4df77afdb642dddf29005f2a0d8f239e79e7baed44b53c9623a9295469085bccca71fc7f96c3ccd6ff34fc5e5acf83093cf2f974d8bffdcd0740cd3cb08ae8208b4966bc4900a95a9ac82d64f3661ac8399f8d8a8ec3a5c4de56e245b5f99da0cbbffcebbdf70943bd9ae613eb0d68f373eda8a14e3367039c127a47fd6b887e392985f65f94c65d35c65afb054c668062dc1bda9ef1bf38981ef828b29bd437064d19b5df3b0b8035bfa16047a31d7621da1be52afdc796d8c928bbb1bf1f9e6bbf8429e891f0621cd55a6171371bb3649d06febbb286a13130a2e6e22be5606858fa2020b08c2e048e01a9c48a8238a95f54d635c73413061a11c2b85a7126238aef49d8cb32ad01dae7d1c725783c68e12b36ca1325a16393bd9b6e4e4922ac02e51df7c00475d0854c7bb87094df9b09545ad6d8291ac7d6c35e7943e76958e9c8592a2ddf86880b07e4eefa99dbae125ad969c0c5af73281ade9592fb941c7b3421dfb95bc53d926a8ecf1928ca546244a8edc1164e9ad85128d77ff053760e995efa898d354f10890aa610458d8de744283760555a5571c6a8c1df97642d55d8278a83a42b0cf069f09fd2504e99af5f37141153c1221f93aa651401fa9e9a719b36f97ca223c1a464c03fc26141a51976b4edfa9e0438caf83a693702f9b7011623b20a27ef2ceb7a47d1fcb2d25f42cfdf813f38367a39eb27e20e4ba9794fd7311f2181da838f9ddcb8cf5d2e9968fdf4d20ae77d1e27b60f37c13f411ec8ef620b4c1d298d2c5c179dcddef76bf018bd61a649a060000" 10 | ], 11 | "headers": { 12 | "content-type": "application/json; charset=UTF-8", 13 | "content-length": "620", 14 | "connection": "close", 15 | "access-control-allow-headers": "Authorization", 16 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 17 | "allow": "GET", 18 | "content-encoding": "gzip", 19 | "date": "Sat, 11 Jun 2016 18:44:57 GMT", 20 | "link": "; rel=\"alternate\"; type=text/html", 21 | "server": "nginx/1.4.6 (Ubuntu)", 22 | "x-batcache": "HIT", 23 | "x-cached": "EXPIRED", 24 | "x-content-type-options": "nosniff", 25 | "x-ec2-instance-id": "i-f737987d", 26 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 27 | "vary": "Cookie", 28 | "x-cache": "Miss from cloudfront", 29 | "via": "1.1 b4462bd98dd9186cca8c54d37f70d629.cloudfront.net (CloudFront)", 30 | "x-amz-cf-id": "fsfzd1XTGJ8eNkmxQSUhvKg2QOBT5C-eTjqWgO3yIU4_2yC7d3GyBw==" 31 | } 32 | } 33 | ] -------------------------------------------------------------------------------- /tests/fixtures/responses/postsByTermSlug: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://demo.wp-api.org:443", 4 | "method": "GET", 5 | "path": "/wp-json/wp/v2/posts?filter%5Bcategories%5D=uncategorized&filter%5Borderby%5D=date&page=1&per_page=4", 6 | "body": "", 7 | "status": 200, 8 | "response": [ 9 | "1f8b0800000000000003ed5d6b731bc795fd2b637ed84f064849a4126b1da618895e6b63c9a228c9b515ba5c436044ce1ac0c0c040a2e3f27fdf73cebdb7bb072229c796edec6eaa129a04667a6edfe7b98f1efded879d76baf3e0cec73bd3ba6f761eecdcddbb737fb47730bab3f762ef0f0fee1e3cd83fd8b12fbfb998f7375e70b1e1323fecac9ac5b45935f87de7b2ef97eb0767bb67bbd366de8ddf2e47f5b21d77ab8bb3dd3f2fff7467e7c78f77e6ddb47dddeaeaeb9f1b17dcfaecf56c73c1e735b359377adbad665350dc7fbfe47696ddbac75fb376f1eded24f1f967bb7b0767bb77f6ce768bb5ce76b95adbcfb0dc607f9ff379959ef7113733e9167db3008b06577dba3cfcaa994dba7953f55df5695d5dae9ad77f3a2bb85332e66ce7f0ab67d5d1b3c7d523f0ac3a6dfb66fde9d96e7d38ae5e5cb6eb0afffbbedbacaad7ed6add57dcdcb83a9eb67dd5adaa69336bfaa66afb8fabfeb25954ebbe5ef5d5f9acbbb86817171f6199e5e1d9829436579366b5bc9dd277c8f8a014d49bfeb25b49ef5e3775bf81c67c336fa66dbdf3608f9c9ccfc1c96fb0837eb386dcba65b3801496d8c73b1faefb76f2edf73b0f5ed7b375f3f1ceeb6e35afa9a7b87931ad57d4850954fba25bb50dd6fadb9daf21cefa82bfe2b76fa819f8fd879d75337b8dcf7ed8a1806ed71548ecbfd7dde26cf7edf26cf7cd5d301682584373767ec492936e366b267ddb2d7ed1725aab3eef36d8cccfa38a2600aa6402242c988ed59af979339dd6e754ea7eb501df7ec6ae37eb669576bd6a963363f0075add9560fd67d24f77811dbcc103c1d7114ca1ef5610facf634c88eb6c77d5bc69b9e25acbbf5d3ea8fbbe9e5c52fb7eeee252e33f2f6b78c2201bebf6cd6aae15fbfaaa5b7473d01e6af93d34f483c823ab79e2d9c73f40d7d303b9ef6fa0fb1fea8134a3f4286afec66dec879d453da7fb7dbbc4b3de512dc681b716097e5835b31f714ddfcc973398290207f5f1c7af7ffc11b42b34edc1236c05a7bd4f5edcbdf7e0dede83fd4f702fbfdc0a10c30bfeb1e09423c1de2767bbcdd5127ea9af47f57abd815e4cf1db6a", 10 | "7209c73ce9bbd1b49b752b44881faf8d65432a6e8865c38b3c96fd84a792693f3bc6fdf49d5d1ffb8e9d2b55e24a95b952892bb784c4cb3b87c77db5e9ab37dd6cb384936fe6d59b668630867dcfda09bc5e4585e85675552fda798bd8857bce169f6e66fc396b0f4f366db56ea655bba81a44423d7133af1083d7cd779bcd02210537e1425c7eb66bb7d9639bbe6aa71585517dc745ea66d954f05e88e63542eaaa7bd34e61bae3ea9528c2759d2d3be91bac5ab508565c01eb80e671f5e5ebd7eda445609e74ab1576d382a63758a0c365697fd5a25b54583e3ed8ccdfddd243443d52450297eb7a5e6d00a5d226b095e36e5dc187835978149883676a5972885c48d7c68611fb27df7eb7e9fae61030e415b4b821d7ebf5b2592d18759deb93cdfcbb4d33ae1e6d26ed7cb3ae6610c0aaed4403b832b73d3f9bd513c4ea0a96dd5793cdb2a559007000d3081e01d43cdccc9675b56e173d78db550d24b20671b316cbe337e3d8d94e89810410dfbe7d3bfebcde4cbe1dc3eb03eef0867a3ec21aa3a6c77fda35cc0f44b760ea9a1f89c266443ef1ebf1653f9f013b656d5caeea660d29b49bf9b87a525f2c5aee5cb78128532e32b73eac8ef0c5600f6446d5ac56c05471c3aa59e136e90bd40b22de2000f1ef7a7cc376be6a2e17cd6adc2e5e7780c03b87dc3df655434fc8247b34253cae9eb697ed0cdacc850bd6af6a62087cff981a675f832dd2816ab18160702bd4aa9302d690e58a028156bcde5cd4586933ad9d01c75011e8f864b32626ca7a893d5db613585db39ae85e3c6f4e1d1b8b2bf888ac8807144c3ac1cedd68e012a132e0a124dead6f62c8e9e4b206ea585d802b9232c4d812b163dbeb515293513b0d692e9a49b306dfb0a7f38dc1e06ad66289ceec1dd0174076093d6e4da7a51eb44d92426ed6e7e3ea7903f30491e00bd505d63969a77010d95e8934687830032c092c3d5fe06237d57575148e1f4b82778bee1cdfcebb192caec5df520db801dd24ce230b90a718e8e0d1b45db6eb496ba47343950bb89ad7572dd28382bd8fa55e73a82d8c8f9a931fb7809a4024f44aabbcb329947d854dddc4fcffac27dd3920abeb63037d184ddb09c29afbd9119c29b474414f50d339c130c2298edd494edd3db85a842f83c660db2d3c43b016da9342a42b3d5c467874f9ea464606f993e7c6f0a6a68d31373191d2384c64102552486a3b59dfc00d6a0d31b1e0da29e2018ca0bb89098f9028348bb5010f695d7ad828ff26e7d34ef149e173c09a70922f7b051e061fae41176cec2253c7e61d610d11c1a9a403eb62e8187e920298318579dc0479cf58de1d92c662397268d350e61ecc867f80f248ebc7d533849fce7c6f531b69d2571009df90022dbc3c318b3d0aeb6a7ddb5bc51de84e0890de16ca8710e782dbe6f6e7262edef2061ad16c71dd327f3af627934700bf5ddf9bd953abdb911ce8c8c33543d10836d7c20d2cfa11881bf55d5fcfc1f573441c18190c7a8a2d7a040a3d33be875aa6a058873ed25b92ff0a7a0a9cc9cd5848c70578347d04554f712b2310d8ddb834cacf10fee088c32adc231746184c16a7e20ff3a75521b1762e445905ddcdb556fbd9aa9eb761ae57034ecddbc5861e13ac6ac0c7513d351c8ad098fc44f8d04c8631ab9e300830c81fd122cd513557a5ffa43a32bacc1503c079782a0f7ff59a112ae24ab568e0d0e84add2b27e033aebe303f0de583a79b622d9884611729d92595877c5f6e90f42b2681742040e86a68ed1abfe09a130891500d814f1e01d88a8a695abc4e7bc0fa1ed54b993d5e5075b654b3801cb337dd8a6e51016981f408648dd6d8d228fdd13877f105763302a619d1ddf523e42bc44350526d0c719af52098940092bbc4129082142259831cd00b6852dabdcb693dc605cb43fc28b01b20ece53d21de9003e01e040ffe3316948fc08db89437acf4f3e0f0a12162a13d321161aef864bd8104978426095ec1cd86588da1526767879c2f4167f1957c31e5d44a622625", 11 | "c05378238f14f025af613a8ac1c41bc417e63e68f420c8e2afec2f1b0af77290f792a0bfa3547c6df01e3f9f422148557201615b1162c179d7e2f2b6637856861cfc5f9b7001a5650d373206174a1f8e1d924e6b05e086e43e1ac10857cd6874f8e9f9aa3adbe506daf905c2600f80fcd810555ac34c82e8833aba5e4dbc32281595825d76b3e9b8edcf760ff6f7aff6efa13e0985c38359cb7329779114613f0206d0416cb896cd25f042bb4b04339b8099d3bb170e8c5c2a2f6140156692834f3850bc82fe0e2e3fdb35329064bdb2600308a50c0a9791125a15adc380b55363595da1ebc8539e15daa2ec8f8ea3c25217d420cb45f8688f6420835a9ddc47690e8403d0416262bb2abb43bf1ba22f20770e0f20f4753d41e682c7f87e508a25fd81864182bb36b1688d4df762b9b2c2e348907147697069d1977dc5142cfb56f37c43fb4c7e140c085850c966c854934342aaa4af769489a857205d23194c9085096d457a7b3e83f785dbc59791ebb8e702e1ba7c88c2e3be356a3848af0ca029658042981191d005960cd0a17c04f0503905ae1a08c83d040048da8ec36fe6d6c7c5c75ba029453ab011265c270518570f8b22006ae47444a5bb72140bbc2a7ae1b7cd28a121c8743d38a3ae10eca5f609aec486b09842d7aa41991ddd0e0234bae3a45c6074f2ab4af90699c031198b5b607f564b607ae711d4a825af1e09a5d18642831de9b95529c8280b0cc1bd83345378e60e127109995a692532170b2d242a888b3ddc109296875bc0b640e247b9c2502a7f4ecf133537a68b9f03fecc66169845db08a6832002dcd38f2c331a1910929c14977dd5353c64195ba11cd264436e1019b035dc1ce2b0f89faca343f126a5962734b0d0e48850a612d09285e19252af609130b975cf843e02286352989523d98629f5b802e9edbcc613ce6129f02be0bb421124cf602684511958530e8f1d441d256b3eaeaae192c6a64b2a8629913559ba2a439598e895a8081a08eb46aadfa1264508517ab020294228f77513803aad51de584d2da3ea2693ba410d1a180999bdd991015300a78ed9a5e34de33da402dc89d591da31ebf6a28f681957708ff8ca2a758e0ba1c550f1be1276dccea1905559b43187a20560e15c03b7842967d615cafa1ccfc7daf8297e424bb2616f6d3c27352fbab728cd9cb77f4785b81fc9658d084f8b42863372249bc6ee6957e687207f109589918d5879d178135fa9ae5223f3792e008e0d59ccc8caa09d8d11c4e7a5804f889c87852923fdf31a293032317561adee8066ca64e4cc47ea15c5124b2d727818293d37938be4c24239418463612f4bd9263c43ae4af467c513199a102305041c4b14985c820508afb6d275317423b1ce4886c692ca0444125ca64699cf0a40b8be01f0f3ac253cbaca1d68ab7a708afc5d5e3f6c3494e784cb85372178d9529794e8a42450a50b7463874c178cfb2fa41563245a4048ac11597aa1fc76145174149e060228b44f094778f19174147aa434037fb8195db9b350a2269b260644b0055fac462e4c08edb34228bc4be108c16c03dfa699f0488c3bb425e8684dd9ad9750094128af12d81565b9df0b168c559d450b60d384d74f596107b109567fe9054b0b76f003062f4dd4dd147d72e4fee50d4790c036cb5546282f52d477ada12b3527c845c5aaf4f453036a6162727eb8fc1a34ef399432a984b1a51a910d717577c255727f25513473f3f4927df9d5ab28c90827a7da592a0f2782a314e3be43bd0758009f3d4896d20d5f86234e7090ee9e89176116436149c723018da4ede557da2b3cabe22c6c0c9500852f16c0caebe84187305125b2d410a17732071d5ebebcfb613433628332666cafbc28726b4ad61c7df61708bb2cec96ba9a6e4d69c95db69cd00085eb0454332c1161169c2c436dd08d452eef32cd4aaaccd00ceabc3cc24a1ea3797a1824f099250d5603d8621308600cf002a3162ab40e37bfa45fb376635a3352cba480286978a463f1b4f4aee91e2cf539a44df451f849040fd64fcbab4e99939937a807ec3e61f7262ae174951674a9ebe5fd4ff18488db0c03169d0c9d95d741913c0647ad974e3bc301535d97d5ad132ae021a495641fca00b65dd737645142aa784b9b90a1185ba59ac3514a2abf574f10998b3963d2935a80d5fbfa7ed587eaecfdeddf38f7d42efffd6b082f86856088378dea7cf2c79f3aab339975d022b4acff99a675f6f6fe35afa399940f337d02dd45452006429cb980041f7262670fb379efccecc404990f907da8f1262d77b6fbc91f7fadd92067d1ef351dc4c7ff66f341616bbfc684d0c10df341770e1edcd9bb6d3e282ef825f341708f23552947f805150b4d2cd4e7390f7ccf7450d0c0e0c351d86b0799e2229f0e7aef337ff16cd07b9f70e354ec11c194520ffc427e30b8d6e7b97e71db5cd081aad25e4ab5ec5279cb53554a411481a69ac0f89da19a35048ffccc2751cfc89527f689a3e802ecc3210b552f552d29cb5208b50052c4c6910c2977400cb4fec3b03c6d78795da6848f093f09728b55afcb049f6c56cbcbefada6e5f9d60854a662494a21aca0106936f1171344368583441087fa95100b871cbc2916d012ff45bb2c325ff22ab109134e83c9184b9995d3a9d1e38548e6d06414ca83aa8aa5b9282679489359b47a986780227f2df9ef2b059af61a4b01b4739719c998f5fe22d9cdea22298f8b4c27771d8938b935d65e8ff9a3603f360c89a61925e78f6a65cc257d04441aa4bd43784c9054e448156435cd0cd32ab18ec2825400ea121aaa61120d9ee90e5832907e14c853c5ff9844c018f03f95ac49716ec7b28c81aa95faae915e96fb896a0498763ed8a8d52c08c32172b204e226bf58f3b30f9933e5fe9ea345a79d09402215ec00bbad78325093171ac6e3ec8b3aa42985213f8f6489488cf8ec00e652a5bc3967bfecc4d85d4ed014c5157116b2c8d585ebec885305c7abe9bcf6c62d8a222cbc0e9e6e0692eb5998a688aa2c8bada9ca69c310457921598a0a20c692e8f4984aab7a47a3540d820661d346f223501e0dc1a92c60850d30e8598d690a0edc2522b241f111a9baa1b5e9313902879cc47362d2c212532195b45b742304d7f1a3f054485b53caf87290db3d75218a5cdc64c9177ec27f4e5132888a454a96a5fb50d9e1b5f00fb498544749df1629f7119502c6603fb9d1f0b09481ea3c74c96ae827e737284f598dc82b4f96585ae9bd4dca689371f4f6d07c50b195b34ba59c8f1a2e602f108122f36f90129f28a8303d35452db6e553a4770f8f6c3e91b682c225a40509d3e8929451d372db857d716614ade9ec8cb87792e0c1c90d22519ec4667381520f8d9bb2839ac881bc6879d8723310cc31d37cb3d2ad8e193ca21e552ef12a8a50502d1b67b0128539c2ec9eca7b543430cf92841fcf2caf3bc63641a1d5e1b00bf244e96d79d1231f5c7357b5554502df0821ca1bb03fab676e31037253a61f7eb2a87c2896b5838ad5d194f19ead8ff05868c1e5c19661ede9e5a0e0e0e804a459a312de35d19754ffcee169f8ff698b7927969d583d034be88dbdfc1465672bcd7174960ef5799ed3e4f5c163aa2df10ebc3aac348df104fd1ca779e62284a4b7648f5b737f52ce1993952c5c6d8d551f83387ab66872e7fabf1b5772165fe419619b45d110ab2ccb7bd0892bb8075203e1491bac263ab8809c614464854c257de92afe1ceabcf5b2cc2b38844a1c6a920e63bbae77e61212d54fac180b3c2a6626dd4ca444b9cd6abf853fc560c25f72afbc98a595058aec2de585d32b5a815e06767f16a3e2de0d8486598d5525451813fa8b666110781a7c104c2d9e2cedd08c28263421b4228e160d58454643e4259e00d9c603773fe6a3af8bb65fd48b0beb5f645d1b05554c793011e5616a145760820f65c5986eb29647599f0d682b292010c5c8294d1b0df9459a8da5ae8352fccfacad08942769002315a50bbbc808c21d39ad9dc159569f714146d685ae13ca84ddb19f8b32a7e2bd0ffd72f2ca27df684684fe68c10113637c", 12 | "da9437c54e951281cfcdcf725a9423ac260e5709ebc3d28f0fc60d1f07488d90495e5d271fa2a1a30da6aaa31bc9057d22701e124192e1eba1d7a49e73fe865025046288c57b2769264c6698a207128fe3c8ba88b30bc4e4b3593287eca3a462f23a593ff25dbc58c373f03d61d2f01791bd0c3a6254048c776a1200c821a29735d995b0386f5d5bd03a3d1a786f86e9478ef952d2129897def71d2306b88ba99a24c671750a378510207f079a927c4fe1af52cb3fb660b8c5710d1a93502da53b635aa00dcd85f698d70b36046190900668925a09ed082502f43b1b52fe6398435132a58dc3162b342dd43b659489e19a95c0de52c375ece32f3e1c6ea9b4fbd7a29528e86a6e2c4655c01ed9ef966a3f8b4336c137ecb7305d1b0e1d765273e7fdaf9b7e72e983b1b9ff3562cc886321a5a7416e10c3f170f2505dd36d8293247f3819e73b3b8f8566961e5511d7663174314876b10c2955cff7214690703e062302b05e8d08a8346437faecae0bd97d27cd234c12df806cb8f7385d84b6ac4f2db14ad1204246fbdbf624ab854cbd016e21498e1fae548a50383c4f488535f2d906650359e6ee34d1777f9c4e3311b9d8907e280f7ca33c786901bc6acb883cab16702c3c407824280eb01199295c0c8dc1c6e101b69113038131ad814962ea45435f60138f61c0ac64f345c84d8f12ee4aa620d465a79b687ed51719f7292349e007c13847dbc2dbd8aca93cbf8d0c014e3cf4ce4da97a098e14ec927195595ca1625120e03e737a2872b7a7eba56474fc3855b399f57f37357377c149018c0e74fe5f0ed8f8c8328298ce4ccdc3dd27ca20c788c1ee8af286e5b9c397198b2c91ba21eff4bef9bdc32776b02567765216b71e826f3adc132551f84681de2d84b01f7a94c9f3f2dd1328b38f0bd89530623bf942ef9ae776601636079d92a91376340932f17ff027013e40c3e43f2d2c67a752baa72818c000d3bd01f78763bf319b7bf7d032251e9733304774370897c720c622953863f5044f30a89b140a2c90e545267f855610a160515ee079a4c5249c3bb64283be91af232fd4c606999c449ecce0d931678cdb2f16b3e63526d8b114a7933fb3633c3613c610932c48d344b78f277f7270b5ff07bc42019e0b4fd47872c17ca6eb0829aae05204ac5c263e42064f035011bc945f044d2a6fc1d0601be44c79c9113e2bd0a6aa6b9edd15e1a9bc83ca607180e986a6004154ba22cbf591a661b2029b9372b6205901ea8ec2b26311190b08d2c7a671606461e527d0740887caaf5c103a1ee71eaec777a738938e91e8b6c120b8a289a78d112dd66edce14e65f6c299a9b09ce508e9807db6793859645cd835a2620a2848d2d3d9529db6b2aa25d3d2c1a8b4b092c0ac79047106ce57957aec3fa33feba60f38c013b5d6e9869bba0ed33ec71cda65b3ea3dcef33816eae431a9c901c3d812cf6b80f538c9e6d1cf916f783896e5e35a0c4867e7832308ed24361fd303a62f8825c0b43af65d560520b53ca796e7c2d77e86d26f884335c6169c6694fdce2d0c9bb1d3145c8852be71e55aaee890b42b918d8063e7f2cc0b14ac7c14e36c10aa4fa32b7986a15dc7d6bf74fd5a039b4a158261a2540143e48d081cc0624210e8873cb4e6e9dc371bcf0c2f981121e4f32aab4e8012d4d654f61b04435864406f55ea68c9a62dc531169fdfdace6639a47add7e18019fd7fda5d9c51075f8c022c8911bc5fe307b97020abb751ee834aebac06e85dd2d36c0222cee6a8224677b3065d7209aef434b260b79d048d270e67514bf5835f0813ed79c052c13aed5408442835589d2753e891413c0389a589e280c4dcf90a16cb998b4324df85bbef9bdc120c6ce6cc6dc8cb8002edcdced0161ef8f57fb7bc3808098ac08ee9a444fee5a6b9121b2a00cbbf224bd65168e83a353e24767734f0a52093fc8b34aae96583d23c7982305270c230cab3ccf62462c49c7cf4d38231030329866d0455c8699e0387800eb306dba1edb57cc951e29fe9913c89e28a9940c81009683d929548249e12ee394a77922a42d8e15e4373ce459e051098959088e88a9f1401678a55c479f518802f6659007cbbc3fe30e019b22d848b6426bceb945ce8320849ccb9a7f23ad6995571e1201e48297fc55ae81d608caf2810b7ec0d863af50c05fd959845620fa1b576c8bc59a4c18400b12026c468cc55279ef39d1cb150d918108788548cd223e2bfe965058344ea71f1ca0a6c9d8a839c09b3dd3d992e013e2807a34265e250f6cd978c6a28c86b99105464b24ddc780e85443d111023242028ac96fb1cafca46fdbcbc1c42f6096361e0c415696943874d198a27e715a20b1258a79103c000de199c16f01236339488402858623f8cb8fe047613c6839157fa1607acc5ab51f7bc7dd8d4189e87be039628ac5109d67c1534cb5f599915314f28031ca8d5275e374837800306187b24db3542f2d9cb3bbebd82def1ea6ee5664f8aa41b91e5d5fc71eeaff0ecedf16aac5308265fcf51365bc036b10441e15a526b5efb1432179b0c35ab4d0c39c69f18419b81fb906580f7ea0209a3ab4610a5e3ba03d94d832768835c890adeda5a4f1af9be5653d8b43009a0048780ab962d22344a26c457e9423b407f1d253c2104118afed5cc57c6e2683e702a3b3ac900ebc9c46601b9877c94cf8f41b14ce72cd34a06bed013e565c267bb265d18d0d076e8f6b65a1b8bcc6e370ac18b4a75ec1a3389b03a3f559066f9ad825de2ab87d5c9594089ea66101a8acc11e33acdf6c9c259d2823b84ac590c0e1f20cffc72749fed1f1d6fd9f3addeaafa7fb279a6d3df8d764ebaf35d96aacfdb073ad38d5fd1b4fb5eeff4a43adc69ddf69a4150fffad065addc07e8d71d6fbf76e9867ddfbe4c1febddbe659e3825f32cf3a4bd5f09120b2a7d1c28f3a13f89e79d6a08101e5c679d6b8c8e759dffbcc5f3ccffade27dc38cffa45e287672ed601313c4d9073db3cebddc3a74c2e12080628d3d018f31ea68c844e793a24a13c2d8ee49197e3fff6e22a141662a444b3463eebc19a32008ad16335274b407873c6ce003656041e22f4c77edcbdc07929570301c5c780eaca6e0cb5a4327354bbd40d894fe34c39f229df02603bde56f6dc5be44220c5c6ade96cb555f6c9e259b89987838de3c4fa8f39e587d6a4a7babc36268fc8caf452a454ee009c467fde6826054c63f85a29e3b07228968e71ea45c7c2edf2c1ae8b0e995a1296ad58f54bf9462ca5ac5d194a9172e44e670c6519eac7c4cf1638cf0dcecfeacbe6fb78f30f2a4148e555d2b5aeb715e6529d32801cb6efa54083df4a6ad501f4dd5853b868a97969443c0cc1a51e70ae709065306530bf98292973d81815b2ecd4aa34c4dda1f4984555115d42573a94c6b24de290b2f240255acc320589f1ba61356353352767ab51913e4a4350f684a23a95a631981f33e7d2cbf5401c0edddbc8aad4201e898fce8d4c95ef4b8d90ee441a1ef96f7627de03ce03213146a7372841e106a53a9803faf5d8f04d6f3063dd055ac5d71af1f597a226551fa1f54af8a464ac21e744ed5a653adabcc6db29509488ee200aa0a9ce3f4292940f29e0d5dcca7f46295be30b1522af8df2d550c5b00dd937544f02b6fa1845cff4fba67c6dff5057dbfbc77c02523ac81b69e34751316016a767f884b352b7cbfdc181cc57a91a132f2f436345658bc8538bfcaf38e298c6bc0e0eb389467b81c715c07c9fea055d5ee5d38b1ba94d46365daade4f94b34fa93774815925c448699519656e724178e648a4b9f050e97d79e5f568fc41e494343884dfa0cce5d7273eaa4d0bd5db07f10b0b7a7090fabbbcf6189fdb452a52955f71199bc5cd077d9dbae048ba3cf1ed3e9abb767440286150d532abb2fd9971c7081b08846ed89c0ef9934a85a7f1be14690f1b0810bae613ef0fe4fd981e1f6b2a2cb0109b5e69d90c0ea6bef407c943ab1441f532660f7b873e679dec026b72c222ed58bd49d4639361400a32d236b5f77344a1bd967716ea494d2ebf3a023da946914c9c5452ded1142cef28e29bb275ab1c881a562fd3a54946073c50ec864da659c19584b88ada0a8370877a6804574c81a683e48800c518949b2dcb05c31dda13b00708da5c6eb13808347bb9aef530e1cb8456a9132d1f21efe7156a6bcde7a6229e818d601ff89705ded789b8bbf787ab3bf7b7de9c750068c6d7ae6e39683a1d8625b600f1ab17680b716c99fcb3e8b747658e1274df13af2d4d15a5a4e51cf88d229f59651491096f922471e30983014327060762f6c57c131e545e487d52115b0ae151a8bce033cdd7442391aec2cbb1a119e5c551fb8a9dc0262cb293294299e5d55e762cc045fa3694d1656e730669361a9e2a4f3bbc578e070777aef6f7710214b5c7fc72b58f46e895d90bd7526bab389994a392eb7531e6996bfc604e7e198ff5baac8f9afcb2e012cab3f6ba0daa073b06d20f7f5332bcb0bdd882f0156e24dea442632820e1d170588196028ce29ed04fd013d117ac377bdb8af1a99afb97fafbf245c3e9be82045114c8440a625a07d14352a853a79b186eecf44036412a480638199029da699c966e962fa1292670e470d41937afcbf4856fda66888fa7c9c0b1bac308d66775b13b6a7badcbb87a69b62e57e20d62dcc41020ca7cba2cb53c249878042e121c8075240029fca4ed21f0e68e10160b7736e84421592b34dba6d48196d21b0d6112851fc4c1a698c24bba538affb9dee403a92b3e5e27d367f8c730f012f289a13612cbd7f3d4eb912277cc3832768cda8555e50ba81653c30ac2265c6823cd36e83279505a29ede1626936ba00b7e195d268295b8904c3f9d641993e65a87c9ecbddb01026270a269964dd5ea05c647d9a6d3598059cc05b3cd4f85a34a5d04f48f498c95db442520e9326c202bd40fb410e81079c34138c62b2f531a60d0370467aa0b0acc401493a1fc9ddc0c7a577c9d95b534134de3ca2ce6fb85224bc0f637c8637455048f1bd643a01500a681aa474960d44637a0a48e2de1af97e99d1690ab2f02e8fa7d7a9d4e925a787fccdc7f09ede683e72f08346b21def8227907120e8fa646761470838617285d500ad383c965f098d290ed6c449aa39358c875c7cbe0cfc49d503ef27a283884091df277f8c4ea45ece77dda6fed2b4f12a7104c84893d1bf2a30c728b4c2865a242dbc613cbd6f09bcf832dedfaf2c8479b3590d63b4474b746c80c94202a180317e8790e01f29469861bb2313da255a038fe4fec439dae68a8a1818a0d8728c5d145698d4d5c708d3a0c7755cf96283f77be14d997662835d3a9d5d00ef732b4f097de1fa6cc3051127b047471bd73d836337ffb1e971fe323fa6b076584ca0ce88b0663acc09a162663308c70345cf0b58eaf84e37f91461d26020ccec949377108ac5b142e2390823d0d2af0c7c37748e600a9ffbdc49925cb1fb5739a92c6f2e80037d02f243fc3b4c061573bdc9b21ea3cae05873ad45fe155326e6e0698e1a9a234318028bcd98e2815a639c45366646f2c179dc355eb40b59d3edc0ae5166901b83bb90457bf0bd1a9790c58d5bf5c502719037aa8898c53baa3156070c92670434c525e6baf00bd9ba1ebc86095acf8a06692252171c2879bf285f149f9ec3e3d3708e69fc94ef484d6789c0221d1755a9c09e49173d084322ea180fb5b4993540a1089319294de37d816606476f78ab329ef04fe6b1709f02e570684038ecc50affd442f7b628b4287e8dc2f15b982663712e0a532151590938ae64c9842c744dc11b0c1323c8c098e4c066b32622b4814f321d71d9144fbc2e3c8de347ff370ab898df211d26279fc4106a8811e4eb9ddd274cd72dc55744c4bdc331bbeaa98f266e7d0c31479a9962c89a5d7f7c418159c3596f9fa49364641654df8691ccff4854f083d4d384dd5d68d729798f5a232fcac92b6f57a2a885f897410c775250758d0c47b026817cc167e6b229af556eb134e63140400add0056fce745c89e043d3e53b2c595ccdd601356adb4b99fa47e20063115fe2c78c421c9c21559c02472f5e9919bdcf19788beeb6f5b53409b56e0", 13 | "093cfda3367a5d6acb51154ad554115e47228e41a37424d5dedb5ee2db28d2987ae22e74348cfb06dce277737d1e256f28ffa589f097b041b8f3805ee69e827d7852ca995f695c4cd8024577a894b2f6923ed365e9931db44ab7c71cf7e5fdc16bd00b075b4cd417f692ab71c0ac3eae06879aa965393c25b114bcc931f953be120054e0b9e531fc13b9c72818d82bf6053a8c838a675097443e78a06491536479c231c66c2c1696173f911e52e9a8e3562580e99697bc62f010e7c141be46df414c79cd97aa259a3473163bb8821e3ad0b57c62f92db28338f869b5b2403914380883472b2f3f8626586729a6118bea5b92206a597a354619904b3972d7e16ccb086a9a4ac78b670eca4f568a78e16fb630cd216740217e0ee289c947992e3780404d33bc75faf52ea65fefdcdf9a7edd3fa45f80a70b541a60a5684a197c844f926730dd4e69555834f9c8fd0c8be1a7f04af47c2031dc0907ca58a710db61b85b8c4ff08c3806e1128566fde339e5652f436acab88d15e5f7276c693457e923aff8dc3a16f5cfd215adfe3ff53fffd1f1a7fb77fff7ce3fddbff7af01a85f6b00ca79fb6127a0eedffbcd47a0eedffd9566a09c41bfd310149ffe5b4d4185997dd031a8afff075b433fe9947a0000" 14 | ], 15 | "headers": { 16 | "content-type": "application/json; charset=UTF-8", 17 | "content-length": "9933", 18 | "connection": "close", 19 | "access-control-allow-headers": "Authorization", 20 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 21 | "allow": "GET", 22 | "content-encoding": "gzip", 23 | "date": "Wed, 15 Jun 2016 04:06:04 GMT", 24 | "link": "; rel=\"next\"", 25 | "server": "nginx/1.4.6 (Ubuntu)", 26 | "x-batcache": "MISS", 27 | "x-cached": "EXPIRED", 28 | "x-content-type-options": "nosniff", 29 | "x-ec2-instance-id": "i-f737987d", 30 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 31 | "x-wp-total": "101", 32 | "x-wp-totalpages": "26", 33 | "vary": "Cookie", 34 | "x-cache": "Miss from cloudfront", 35 | "via": "1.1 9f21952d33d91266890f2ab34b849a9d.cloudfront.net (CloudFront)", 36 | "x-amz-cf-id": "ZqgbgoWPES25_xPTnaRYm0r-q2LAVDcZgRhmY04SXxdnq1aGUL2B7g==" 37 | } 38 | } 39 | ] -------------------------------------------------------------------------------- /tests/fixtures/responses/recentPostsBasic: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://demo.wp-api.org:443", 4 | "method": "GET", 5 | "path": "/wp-json/wp/v2/posts?filter%5Borderby%5D=date&page=1&per_page=4", 6 | "body": "", 7 | "status": 200, 8 | "response": [ 9 | "1f8b0800000000000003ed5d6b731bc795fd2b637ed84f064849a4126b1da618895e6b63c9a228c9b515ba5c436044ce1ac0c0c040a2e3f27fdf73cebdb7bb072229c796edec6eaa129a04667a6edfe7b98f1efded879d76baf3e0cec73bd3ba6f761eecdcddbb737fb47730bab3f762ef0f0fee1e3cd83fd8b12fbfb998f7375e70b1e1323fecac9ac5b45935f87de7b2ef97eb0767bb67bbd366de8ddf2e47f5b21d77ab8bb3dd3f2fff7467e7c78f77e6ddb47dddeaeaeb9f1b17dcfaecf56c73c1e735b359377adbad665350dc7fbfe47696ddbac75fb376f1eded24f1f967bb7b0767bb77f6ce768bb5ce76b95adbcfb0dc607f9ff379959ef7113733e9167db3008b06577dba3cfcaa994dba7953f55df5695d5dae9ad77f3a2bb85332e66ce7f0ab67d5d1b3c7d523f0ac3a6dfb66fde9d96e7d38ae5e5cb6eb0afffbbedbacaad7ed6add57dcdcb83a9eb67dd5adaa69336bfaa66afb8fabfeb25954ebbe5ef5d5f9acbbb86817171f6199e5e1d9829436579366b5bc9dd277c8f8a014d49bfeb25b49ef5e3775bf81c67c336fa66dbdf3608f9c9ccfc1c96fb0837eb386dcba65b3801496d8c73b1faefb76f2edf73b0f5ed7b375f3f1ceeb6e35afa9a7b87931ad57d4850954fba25bb50dd6fadb9daf21cefa82bfe2b76fa819f8fd879d75337b8dcf7ed8a1806ed71548ecbfd7dde26cf7edf26cf7cd5d301682584373767ec492936e366b267ddb2d7ed1725aab3eef36d8cccfa38a2600aa6402242c988ed59af979339dd6e754ea7eb501df7ec6ae37eb669576bd6a963363f0075add9560fd67d24f77811dbcc103c1d7114ca1ef5610facf634c88eb6c77d5bc69b9e25acbbf5d3ea8fbbe9e5c52fb7eeee252e33f2f6b78c2201bebf6cd6aae15fbfaaa5b7473d01e6af93d34f483c823ab79e2d9c73f40d7d303b9ef6fa0fb1fea8134a3f4286afec66dec879d453da7fb7dbbc4b3de512dc681b716097e5835b31f714ddfcc973398290207f5f1c7af7ffc11b42b34edc1236c05a7bd4f5edcbdf7e0dede83fd4f702fbfdc0a10c30bfeb1e09423c1de2767bbcdd5127ea9af47f57abd815e4cf1db6a7209c73ce9bbd1b49b752b44881faf8d65432a6e8865c38b3c96fd84a792693f3bc6fdf49d5d1ffb8e9d2b55e24a95b952892bb784c4cb3b87c77db5e9ab37dd6cb384936fe6d59b668630867dcfda09bc5e4585e85675552fda798bd8857bce169f6e66fc396b0f4f366db56ea655bba81a44423d7133af1083d7cd779bcd02210537e1425c7eb66bb7d9639bbe6aa71585517dc745ea66d954f05e88e63542eaaa7bd34e61bae3ea9528c2759d2d3be91bac5ab508565c01eb80e671f5e5ebd7eda445609e74ab1576d382a63758a0c365697fd5a25b54583e3ed8ccdfddd243443d52450297eb7a5e6d00a5d226b095e36e5dc187835978149883676a5972885c48d7c68611fb27df7eb7e9fae61030e415b4b821d7ebf5b2592d18759deb93cdfcbb4d33ae1e6d26ed7cb3ae6610c0aaed4403b832b73d3f9bd513c4ea0a96dd5793cdb2a559007000d3081e01d43cdccc9675b56e173d78db550d24b20671b316cbe337e3d8d94e89810410dfbe7d3bfebcde4cbe1dc3eb03eef0867a3ec21aa3a6c77fda35cc0f44b760ea9a1f89c266443ef1ebf1653f9f013b656d5caeea660d29b49bf9b87a525f2c5aee5cb78128532e32b73eac8ef0c5600f6446d5ac56c05471c3aa59e136e90bd40b22de2000f1ef7a7cc376be6a2e17cd6adc2e5e7780c03b87dc3df655434fc8247b34253cae9eb697ed0cdacc850bd6af6a62087cff981a675f832dd2816ab18160702bd4aa9302d690e58a028156bcde5cd4586933ad9d01c75011e8f864b32626ca7a893d5db613585db39ae85e3c6f4e1d1b8b2bf888ac8807144c3ac1cedd68e012a132e0a124dead6f62c8e9e4b206ea585d802b9232c4d812b163dbeb515293513b0d692e9a49b306dfb0a7f38dc1e06ad66289ceec1dd0174076093d6e4da7a51eb44d92426ed6e7e3ea7903f30491e00bd505d63969a77010d95e8934687830032c092c3d5fe06237d57575148e1f4b82778bee1cdfcebb192caec5df520db801dd24ce230b90a718e8e0d1b45db6eb496ba47343950bb89ad7572dd28382bd8fa55e73a82d8c8f9a931fb7809a4024f44aabbcb329947d854dddc4fcffac27dd3920abeb63037d184ddb09c29afbd9119c29b474414f50d339c130c2298edd494edd3db85a842f83c660db2d3c43b016da9342a42b3d5c467874f9ea464606f993e7c6f0a6a68d31373191d2384c64102552486a3b59dfc00d6a0d31b1e0da29e2018ca0bb89098f9028348bb5010f695d7ad828ff26e7d34ef149e173c09a70922f7b051e061fae41176cec2253c7e61d610d11c1a9a403eb62e8187e920298318579dc0479cf58de1d92c662397268d350e61ecc867f80f248ebc7d533849fce7c6f531b69d2571009df90022dbc3c318b3d0aeb6a7ddb5bc51de84e0890de16ca8710e782dbe6f6e7262edef2061ad16c71dd327f3af627934700bf5ddf9bd953abdb911ce8c8c33543d10836d7c20d2cfa11881bf55d5fcfc1f573441c18190c7a8a2d7a040a3d33be875aa6a058873ed25b92ff0a7a0a9cc9cd5848c70578347d04554f712b2310d8ddb834cacf10fee088c32adc231746184c16a7e20ff3a75521b1762e445905ddcdb556fbd9aa9eb761ae57034ecddbc5861e13ac6ac0c7513d351c8ad098fc44f8d04c8631ab9e300830c81fd122cd513557a5ffa43a32bacc1503c079782a0f7ff59a112ae24ab568e0d0e84add2b27e033aebe303f0de583a79b622d9884611729d92595877c5f6e90f42b2681742040e86a68ed1abfe09a130891500d814f1e01d88a8a695abc4e7bc0fa1ed54b993d5e5075b654b3801cb337dd8a6e51016981f408648dd6d8d228fdd13877f105763302a619d1ddf523e42bc44350526d0c719af52098940092bbc4129082142259831cd00b6852dabdcb693dc605cb43fc28b01b20ece53d21de9003e01e040ffe3316948fc08db89437acf4f3e0f0a12162a13d321161aef864bd8104978426095ec1cd86588da1526767879c2f4167f1957c31e5d44a622625c05378238f14f025af613a8ac1c41bc417e63e68f420c8e2afec", 10 | "2f1b0af77290f792a0bfa3547c6df01e3f9f422148557201615b1162c179d7e2f2b6637856861cfc5f9b7001a5650d373206174a1f8e1d924e6b05e086e43e1ac10857cd6874f8e9f9aa3adbe506daf905c2600f80fcd810555ac34c82e8833aba5e4dbc32281595825d76b3e9b8edcf760ff6f7aff6efa13e0985c38359cb7329779114613f0206d0416cb896cd25f042bb4b04339b8099d3bb170e8c5c2a2f6140156692834f3850bc82fe0e2e3fdb35329064bdb2600308a50c0a9791125a15adc380b55363595da1ebc8539e15daa2ec8f8ea3c25217d420cb45f8688f6420835a9ddc47690e8403d0416262bb2abb43bf1ba22f20770e0f20f4753d41e682c7f87e508a25fd81864182bb36b1688d4df762b9b2c2e348907147697069d1977dc5142cfb56f37c43fb4c7e140c085850c966c854934342aaa4af769489a857205d23194c9085096d457a7b3e83f785dbc59791ebb8e702e1ba7c88c2e3be356a3848af0ca029658042981191d005960cd0a17c04f0503905ae1a08c83d040048da8ec36fe6d6c7c5c75ba029453ab011265c270518570f8b22006ae47444a5bb72140bbc2a7ae1b7cd28a121c8743d38a3ae10eca5f609aec486b09842d7aa41991ddd0e0234bae3a45c6074f2ab4af90699c031198b5b607f564b607ae711d4a825af1e09a5d18642831de9b95529c8280b0cc1bd83345378e60e127109995a692532170b2d242a888b3ddc109296875bc0b640e247b9c2502a7f4ecf133537a68b9f03fecc66169845db08a6832002dcd38f2c331a1910929c14977dd5353c64195ba11cd264436e1019b035dc1ce2b0f89faca343f126a5962734b0d0e48850a612d09285e19252af609130b975cf843e02286352989523d98629f5b802e9edbcc613ce6129f02be0bb421124cf602684511958530e8f1d441d256b3eaeaae192c6a64b2a8629913559ba2a439598e895a8081a08eb46aadfa1264508517ab020294228f77513803aad51de584d2da3ea2693ba410d1a180999bdd991015300a78ed9a5e34de33da402dc89d591da31ebf6a28f681957708ff8ca2a758e0ba1c550f1be1276dccea1905559b43187a20560e15c03b7842967d615cafa1ccfc7daf8297e424bb2616f6d3c27352fbab728cd9cb77f4785b81fc9658d084f8b42863372249bc6ee6957e687207f109589918d5879d178135fa9ae5223f3792e008e0d59ccc8caa09d8d11c4e7a5804f889c87852923fdf31a293032317561adee8066ca64e4cc47ea15c5124b2d727818293d37938be4c24239418463612f4bd9263c43ae4af467c513199a102305041c4b14985c820508afb6d275317423b1ce4886c692ca0444125ca64699cf0a40b8be01f0f3ac253cbaca1d68ab7a708afc5d5e3f6c3494e784cb85372178d9529794e8a42450a50b7463874c178cfb2fa41563245a4048ac11597aa1fc76145174149e060228b44f094778f19174147aa434037fb8195db9b350a2269b260644b0055fac462e4c08edb34228bc4be108c16c03dfa699f0488c3bb425e8684dd9ad9750094128af12d81565b9df0b168c559d450b60d384d74f596107b109567fe9054b0b76f003062f4dd4dd147d72e4fee50d4790c036cb5546282f52d477ada12b3527c845c5aaf4f453036a6162727eb8fc1a34ef399432a984b1a51a910d717577c255727f25513473f3f4927df9d5ab28c90827a7da592a0f2782a314e3be43bd0758009f3d4896d20d5f86234e7090ee9e89176116436149c723018da4ede557da2b3cabe22c6c0c9500852f16c0caebe84187305125b2d410a17732071d5ebebcfb613433628332666cafbc28726b4ad61c7df61708bb2cec96ba9a6e4d69c95db69cd00085eb0454332c1161169c2c436dd08d452eef32cd4aaaccd00ceabc3cc24a1ea3797a1824f099250d5603d8621308600cf002a3162ab40e37bfa45fb376635a3352cba480286978a463f1b4f4aee91e2cf539a44df451f849040fd64fcbab4e99939937a807ec3e61f7262ae174951674a9ebe5fd4ff18488db0c03169d0c9d95d741913c0647ad974e3bc301535d97d5ad132ae021a495641fca00b65dd737645142aa784b9b90a1185ba59ac3514a2abf574f10998b3963d2935a80d5fbfa7ed587eaecfdeddf38f7d42efffd6b082f86856088378dea7cf2c79f3aab339975d022b4acff99a675f6f6fe35afa399940f337d02dd45452006429cb980041f7262670fb379efccecc404990f907da8f1262d77b6fbc91f7fadd92067d1ef351dc4c7ff66f341616bbfc684d0c10df341770e1edcd9bb6d3e282ef825f341708f23552947f805150b4d2cd4e7390f7ccf7450d0c0e0c351d86b0799e2229f0e7aef337ff16cd07b9f70e354ec11c194520ffc427e30b8d6e7b97e71db5cd081aad25e4ab5ec5279cb53554a411481a69ac0f89da19a35048ffccc2751cfc89527f689a3e802ecc3210b552f552d29cb5208b50052c4c6910c2977400cb4fec3b03c6d78795da6848f093f09728b55afcb049f6c56cbcbefada6e5f9d60854a662494a21aca0106936f1171344368583441087fa95100b871cbc2916d012ff45bb2c325ff22ab109134e83c9184b9995d3a9d1e38548e6d06414ca83aa8aa5b9282679489359b47a986780227f2df9ef2b059af61a4b01b4739719c998f5fe22d9cdea22298f8b4c27771d8938b935d65e8ff9a3603f360c89a61925e78f6a65cc257d04441aa4bd43784c9054e448156435cd0cd32ab18ec2825400ea121aaa61120d9ee90e5832907e14c853c5ff9844c018f03f95ac49716ec7b28c81aa95faae915e96fb896a0498763ed8a8d52c08c32172b204e226bf58f3b30f9933e5fe9ea345a79d09402215ec00bbad78325093171ac6e3ec8b3aa42985213f8f6489488cf8ec00e652a5bc3967bfecc4d85d4ed014c5157116b2c8d585ebec885305c7abe9bcf6c62d8a222cbc0e9e6e0692eb5998a688aa2c8bada9ca69c310457921598a0a20c692e8f4984aab7a47a3540d820661d346f223501e0dc1a92c60850d30e8598d690a0edc2522b241f111a9baa1b5e9313902879cc47362d2c212532195b45b742304d7f1a3f054485b53caf87290db3d75218a5cdc64c9177ec27f4e5132888a454a96a5fb50d9e1b5f00fb498544749df1629f7119502c6603fb9d1f0b09481ea3c74c96ae827e737284f598dc82b4f96585ae9bd4dca689371f4f6d07c50b195b34ba59c8f1a2e602f108122f36f90129f28a8303d35452db6e553a4770f8f6c3e91b682c225a40509d3e8929451d372db857d716614ade9ec8cb87792e0c1c90d22519ec4667381520f8d9bb2839ac881bc6879d8723310cc31d37cb3d2ad8e193ca21e552ef12a8a50502d1b67b0128539c2ec9eca7b543430cf92841fcf2caf3bc63641a1d5e1b00bf244e96d79d1231f5c7357b5554502df0821ca1bb03fab676e31037253a61f7eb2a87c2896b5838ad5d194f19ead8ff05868c1e5c19661ede9e5a0e0e0e804a459a312de35d19754ffcee169f8ff698b7927969d583d034be88dbdfc1465672bcd7174960ef5799ed3e4f5c163aa2df10ebc3aac348df104fd1ca779e62284a4b7648f5b737f52ce1993952c5c6d8d551f83387ab66872e7fabf1b5772165fe419619b45d110ab2ccb7bd0892bb8075203e1491bac263ab8809c614464854c257de92afe1ceabcf5b2cc2b38844a1c6a920e63bbae77e61212d54fac180b3c2a6626dd4ca444b9cd6abf853fc560c25f72afbc98a595058aec2de585d32b5a815e06767f16a3e2de0d8486598d5525451813fa8b666110781a7c104c2d9e2cedd08c28263421b4228e160d58454643e4259e00d9c603773fe6a3af8bb65fd48b0beb5f645d1b05554c793011e5616a145760820f65c5986eb29647599f0d682b292010c5c8294d1b0df9459a8da5ae8352fccfacad08942769002315a50bbbc808c21d39ad9dc159569f714146d685ae13ca84ddb19f8b32a7e2bd0ffd72f2ca27df684684fe68c10113637cda9437c54e951281cfcdcf725a9423ac260e5709ebc3d28f0fc6", 11 | "0d1f07488d90495e5d271fa2a1a30da6aaa31bc9057d22701e124192e1eba1d7a49e73fe865025046288c57b2769264c6698a207128fe3c8ba88b30bc4e4b3593287eca3a462f23a593ff25dbc58c373f03d61d2f01791bd0c3a6254048c776a1200c821a29735d995b0386f5d5bd03a3d1a786f86e9478ef952d2129897def71d2306b88ba99a24c671750a378510207f079a927c4fe1af52cb3fb660b8c5710d1a93502da53b635aa00dcd85f698d70b36046190900668925a09ed082502f43b1b52fe6398435132a58dc3162b342dd43b659489e19a95c0de52c375ece32f3e1c6ea9b4fbd7a29528e86a6e2c4655c01ed9ef966a3f8b4336c137ecb7305d1b0e1d765273e7fdaf9b7e72e983b1b9ff3562cc886321a5a7416e10c3f170f2505dd36d8293247f3819e73b3b8f8566961e5511d7663174314876b10c2955cff7214690703e062302b05e8d08a8346437faecae0bd97d27cd234c12df806cb8f7385d84b6ac4f2db14ad1204246fbdbf624ab854cbd016e21498e1fae548a50383c4f488535f2d906650359e6ee34d1777f9c4e3311b9d8907e280f7ca33c786901bc6acb883cab16702c3c407824280eb01199295c0c8dc1c6e101b69113038131ad814962ea45435f60138f61c0ac64f345c84d8f12ee4aa620d465a79b687ed51719f7292349e007c13847dbc2dbd8aca93cbf8d0c014e3cf4ce4da97a098e14ec927195595ca1625120e03e737a2872b7a7eba56474fc3855b399f57f37357377c149018c0e74fe5f0ed8f8c8328298ce4ccdc3dd27ca20c788c1ee8af286e5b9c397198b2c91ba21eff4bef9bdc32776b02567765216b71e826f3adc132551f84681de2d84b01f7a94c9f3f2dd1328b38f0bd89530623bf942ef9ae776601636079d92a91376340932f17ff027013e40c3e43f2d2c67a752baa72818c000d3bd01f78763bf319b7bf7d032251e9733304774370897c720c622953863f5044f30a89b140a2c90e545267f855610a160515ee079a4c5249c3bb64283be91af232fd4c606999c449ecce0d931678cdb2f16b3e63526d8b114a7933fb3633c3613c610932c48d344b78f277f7270b5ff07bc42019e0b4fd47872c17ca6eb0829aae05204ac5c263e42064f035011bc945f044d2a6fc1d0601be44c79c9113e2bd0a6aa6b9edd15e1a9bc83ca607180e986a6004154ba22cbf591a661b2029b9372b6205901ea8ec2b26311190b08d2c7a671606461e527d0740887caaf5c103a1ee71eaec777a738938e91e8b6c120b8a289a78d112dd66edce14e65f6c299a9b09ce508e9807db6793859645cd835a2620a2848d2d3d9529db6b2aa25d3d2c1a8b4b092c0ac79047106ce57957aec3fa33feba60f38c013b5d6e9869bba0ed33ec71cda65b3ea3dcef33816eae431a9c901c3d812cf6b80f538c9e6d1cf916f783896e5e35a0c4867e7832308ed24361fd303a62f8825c0b43af65d560520b53ca796e7c2d77e86d26f884335c6169c6694fdce2d0c9bb1d3145c8852be71e55aaee890b42b918d8063e7f2cc0b14ac7c14e36c10aa4fa32b7986a15dc7d6bf74fd5a039b4a158261a2540143e48d081cc0624210e8873cb4e6e9dc371bcf0c2f981121e4f32aab4e8012d4d654f61b04435864406f55ea68c9a62dc531169fdfdace6639a47add7e18019fd7fda5d9c51075f8c022c8911bc5fe307b97020abb751ee834aebac06e85dd2d36c0222cee6a8224677b3065d7209aef434b260b79d048d270e67514bf5835f0813ed79c052c13aed5408442835589d2753e891413c0389a589e280c4dcf90a16cb998b4324df85bbef9bdc120c6ce6cc6dc8cb8002edcdced0161ef8f57fb7bc3808098ac08ee9a444fee5a6b9121b2a00cbbf224bd65168e83a353e24767734f0a52093fc8b34aae96583d23c7982305270c230cab3ccf62462c49c7cf4d38231030329866d0455c8699e0387800eb306dba1edb57cc951e29fe9913c89e28a9940c81009683d929548249e12ee394a77922a42d8e15e4373ce4", 12 | "59e051098959088e88a9f1401678a55c479f518802f6659007cbbc3fe30e019b22d848b6426bceb945ce8320849ccb9a7f23ad6995571e1201e48297fc55ae81d608caf2810b7ec0d863af50c05fd959845620fa1b576c8bc59a4c18400b12026c468cc55279ef39d1cb150d918108788548cd223e2bfe965058344ea71f1ca0a6c9d8a839c09b3dd3d992e013e2807a34265e250f6cd978c6a28c86b99105464b24ddc780e85443d111023242028ac96fb1cafca46fdbcbc1c42f6096361e0c415696943874d198a27e715a20b1258a79103c000de199c16f01236339488402858623f8cb8fe047613c6839157fa1607acc5ab51f7bc7dd8d4189e87be039628ac5109d67c1534cb5f599915314f28031ca8d5275e374837800306187b24db3542f2d9cb3bbebd82def1ea6ee5664f8aa41b91e5d5fc71eeaff0ecedf16aac5308265fcf51365bc036b10441e15a526b5efb1432179b0c35ab4d0c39c69f18419b81fb906580f7ea0209a3ab4610a5e3ba03d94d832768835c890adeda5a4f1af9be5653d8b43009a0048780ab962d22344a26c457e9423b407f1d253c2104118afed5cc57c6e2683e702a3b3ac900ebc9c46601b9877c94cf8f41b14ce72cd34a06bed013e565c267bb265d18d0d076e8f6b65a1b8bcc6e370ac18b4a75ec1a3389b03a3f559066f9ad825de2ab87d5c9594089ea66101a8acc11e33acdf6c9c259d2823b84ac590c0e1f20cffc72749fed1f1d6fd9f3addeaafa7fb279a6d3df8d764ebaf35d96aacfdb073ad38d5fd1b4fb5eeff4a43adc69ddf69a4150fffad065addc07e8d71d6fbf76e9867ddfbe4c1febddbe659e3825f32cf3a4bd5f09120b2a7d1c28f3a13f89e79d6a08101e5c679d6b8c8e759dffbcc5f3ccffade27dc38cffa45e287672ed601313c4d9073db3cebddc3a74c2e12080628d3d018f31ea68c844e793a24a13c2d8ee49197e3fff6e22a141662a444b3463eebc19a32008ad16335274b407873c6ce003656041e22f4c77edcbdc07929570301c5c780eaca6e0cb5a4327354bbd40d894fe34c39f229df02603bde56f6dc5be44220c5c6ade96cb555f6c9e259b89987838de3c4fa8f39e587d6a4a7babc36268fc8caf452a454ee009c467fde6826054c63f85a29e3b07228968e71ea45c7c2edf2c1ae8b0e995a1296ad58f54bf9462ca5ac5d194a9172e44e670c6519eac7c4cf1638cf0dcecfeacbe6fb78f30f2a4148e555d2b5aeb715e6529d32801cb6efa54083df4a6ad501f4dd5853b868a97969443c0cc1a51e70ae709065306530bf98292973d81815b2ecd4aa34c4dda1f4984555115d42573a94c6b24de290b2f240255acc320589f1ba61356353352767ab51913e4a4350f684a23a95a631981f33e7d2cbf5401c0edddbc8aad4201e898fce8d4c95ef4b8d90ee441a1ef96f7627de03ce03213146a7372841e106a53a9803faf5d8f04d6f3063dd055ac5d71af1f597a226551fa1f54af8a464ac21e744ed5a653adabcc6db29509488ee200aa0a9ce3f4292940f29e0d5dcca7f46295be30b1522af8df2d550c5b00dd937544f02b6fa1845cff4fba67c6dff5057dbfbc77c02523ac81b69e34751316016a767f884b352b7cbfdc181cc57a91a132f2f436345658bc8538bfcaf38e298c6bc0e0eb389467b81c715c07c9fea055d5ee5d38b1ba94d46365daade4f94b34fa93774815925c448699519656e724178e648a4b9f050e97d79e5f568fc41e494343884dfa0cce5d7273eaa4d0bd5db07f10b0b7a7090fabbbcf6189fdb452a52955f71199bc5cd077d9dbae048ba3cf1ed3e9abb767440286150d532abb2fd9971c7081b08846ed89c0ef9934a85a7f1be14690f1b0810bae613ef0fe4fd981e1f6b2a2cb0109b5e69d90c0ea6bef407c943ab1441f532660f7b873e679dec026b72c222ed58bd49d4639361400a32d236b5f77344a1bd967716ea494d2ebf3a023da946914c9c5452ded1142cef28e29bb275ab1c881a562fd3a54946073c50ec864da659c19584b88ada0a8370877a6804574c81a683e48800c518949b2dcb05c31dda13b00708da5c6eb13808347bb9aef530e1cb8456a9132d1f21efe7156a6bcde7a6229e818d601ff89705ded789b8bbf787ab3bf7b7de9c750068c6d7ae6e39683a1d8625b600f1ab17680b716c99fcb3e8b747658e1274df13af2d4d15a5a4e51cf88d229f59651491096f922471e30983014327060762f6c57c131e545e487d52115b0ae151a8bce033cdd7442391aec2cbb1a119e5c551fb8a9dc0262cb293294299e5d55e762cc045fa3694d1656e730669361a9e2a4f3bbc578e070777aef6f7710214b5c7fc72b58f46e895d90bd7526bab389994a392eb7531e6996bfc604e7e198ff5baac8f9a", 13 | "fcb2e012cab3f6ba0daa073b06d20f7f5332bcb0bdd882f0156e24dea442632820e1d170588196028ce29ed04fd013d117ac377bdb8af1a99afb97fafbf245c3e9be82045114c8440a625a07d14352a853a79b186eecf44036412a480638199029da699c966e962fa1292670e470d41937afcbf4856fda66888fa7c9c0b1bac308d66775b13b6a7badcbb87a69b62e57e20d62dcc41020ca7cba2cb53c249878042e121c8075240029fca4ed21f0e68e10160b7736e84421592b34dba6d48196d21b0d6112851fc4c1a698c24bba538affb9dee403a92b3e5e27d367f8c730f012f289a13612cbd7f3d4eb912277cc3832768cda8555e50ba81653c30ac2265c6823cd36e83279505a29ede1626936ba00b7e195d268295b8904c3f9d641993e65a87c9ecbddb01026270a269964dd5ea05c647d9a6d3598059cc05b3cd4f85a34a5d04f48f498c95db442520e9326c202bd40fb410e81079c34138c62b2f531a60d0370467aa0b0acc401493a1fc9ddc0c7a577c9d95b534134de3ca2ce6fb85224bc0f637c8637455048f1bd643a01500a681aa474960d44637a0a48e2de1af97e99d1690ab2f02e8fa7d7a9d4e925a787fccdc7f09ede683e72f08346b21def8227907120e8fa646761470838617285d500ad383c965f098d290ed6c449aa39358c875c7cbe0cfc49d503ef27a283884091df277f8c4ea45ece77dda6fed2b4f12a7104c84893d1bf2a30c728b4c2865a242dbc613cbd6f09bcf832dedfaf2c8479b3590d63b4474b746c80c94202a180317e8790e01f29469861bb2313da255a038fe4fec439dae68a8a1818a0d8728c5d145698d4d5c708d3a0c7755cf96283f77be14d997662835d3a9d5d00ef732b4f097de1fa6cc3051127b047471bd73d836337ffb1e971fe323fa6b076584ca0ce88b0663acc09a162663308c70345cf0b58eaf84e37f91461d26020ccec949377108ac5b142e2390823d0d2af0c7c37748e600a9ffbdc49925cb1fb5739a92c6f2e80037d02f243fc3b4c061573bdc9b21ea3cae05873ad45fe155326e6e0698e1a9a234318028bcd98e2815a639c45366646f2c179dc355eb40b59d3edc0ae5166901b83bb90457bf0bd1a9790c58d5bf5c502719037aa8898c53baa3156070c92670434c525e6baf00bd9ba1ebc86095acf8a06692252171c2879bf285f149f9ec3e3d3708e69fc94ef484d6789c0221d1755a9c09e49173d084322ea180fb5b4993540a1089319294de37d816606476f78ab329ef04fe6b1709f02e570684038ecc50affd442f7b628b4287e8dc2f15b982663712e0a532151590938ae64c9842c744dc11b0c1323c8c098e4c066b32622b4814f321d71d9144fbc2e3c8de347ff370ab898df211d26279fc4106a8811e4eb9ddd274cd72dc55744c4bdc331bbeaa98f266e7d0c31479a9962c89a5d7f7c418159c3596f9fa49364641654df8691ccff4854f083d4d384dd5d68d729798f5a232fcac92b6f57a2a885f897410c775250758d0c47b026817cc167e6b229af556eb134e63140400add0056fce745c89e043d3e53b2c595ccdd601356adb4b99fa47e20063115fe2c78c421c9c21559c02472f5e9919bdcf19788beeb6f5b53409b56e0093cfda3367a5d6acb51154ad554115e47228e41a37424d5dedb5ee2db28d2987ae22e74348cfb06dce277737d1e256f28ffa589f097b041b8f3805ee69e827d7852ca995f695c4cd8024577a894b2f6923ed365e9931db44ab7c71cf7e5fdc16bd00b075b4cd417f692ab71c0ac3eae06879aa965393c25b114bcc931f953be120054e0b9e531fc13b9c72818d82bf6053a8c838a675097443e78a06491536479c231c66c2c1696173f911e52e9a8e3562580e99697bc62f010e7c141be46df414c79cd97aa259a3473163bb8821e3ad0b57c62f92db28338f869b5b2403914380883472b2f3f8626586729a6118bea5b92206a597a354619904b3972d7e16ccb086a9a4ac78b670eca4f568a78e16fb630cd216740217e0ee289c947992e3780404d33bc75faf52ea65fefdcdf9a7edd3fa45f80a70b541a60a5684a197c844f926730dd4e69555834f9c8fd0c8be1a7f04af47c2031dc0907ca58a710db61b85b8c4ff08c3806e1128566fde339e5652f436acab88d15e5f7276c693457e923aff8dc3a16f5cfd215adfe3ff53fffd1f1a7fb77fff7ce3fddbff7af01a85f6b00ca79fb6127a0eedffbcd47a0eedffd9566a09c41bfd310149ffe5b4d4185997dd031a8afff075b433fe9947a0000" 14 | ], 15 | "headers": { 16 | "content-type": "application/json; charset=UTF-8", 17 | "transfer-encoding": "chunked", 18 | "connection": "close", 19 | "access-control-allow-headers": "Authorization", 20 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 21 | "allow": "GET", 22 | "content-encoding": "gzip", 23 | "date": "Sat, 11 Jun 2016 18:39:51 GMT", 24 | "link": "; rel=\"next\"", 25 | "server": "nginx/1.4.6 (Ubuntu)", 26 | "x-batcache": "HIT", 27 | "x-cached": "EXPIRED", 28 | "x-content-type-options": "nosniff", 29 | "x-ec2-instance-id": "i-f737987d", 30 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 31 | "x-wp-total": "101", 32 | "x-wp-totalpages": "26", 33 | "vary": "Cookie", 34 | "x-cache": "Miss from cloudfront", 35 | "via": "1.1 e9805f6e7e7fc960be065e8aee90e3c4.cloudfront.net (CloudFront)", 36 | "x-amz-cf-id": "EojUBXsPZ6gjQcG7hY6lrxhtifRqPJov4C-LJ7hf0y6SSSMkzmszVA==" 37 | } 38 | } 39 | ] -------------------------------------------------------------------------------- /tests/fixtures/responses/taxonomiesMeta: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://demo.wp-api.org:443", 4 | "method": "GET", 5 | "path": "/wp-json/wp/v2/taxonomies/category", 6 | "body": "", 7 | "status": 200, 8 | "response": [ 9 | "1f8b08000000000000039d90c16ec4200c44ffc5e774917ae4dacf585615226e420bd802a7e92ae2df6b9aeda9b7deacc11edecc01c567040b2f5e70a11ab1c1042d6d8b6ae1d4eeaaccd8428d2c918a3ea82077d6557b05a626709b608d587d0d6b0c3e8195bae104af29960fdd3a20504a18cef3eb016bc537f55945b859679c9931d365e727cff142757146e7f746650cce7c3e3b23fe8b0ae501d8f5bb9d6d14cc03e15f768f6cbf7661fb893ecc1e85ecac29ff700ebefd243c2aa63e9ac0cc499b9acfd4fdd6fb373b384fdf56010000" 10 | ], 11 | "headers": { 12 | "content-type": "application/json; charset=UTF-8", 13 | "content-length": "211", 14 | "connection": "close", 15 | "access-control-allow-headers": "Authorization", 16 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 17 | "allow": "GET", 18 | "content-encoding": "gzip", 19 | "date": "Wed, 15 Jun 2016 03:14:57 GMT", 20 | "server": "nginx/1.4.6 (Ubuntu)", 21 | "x-batcache": "MISS", 22 | "x-cached": "MISS", 23 | "x-content-type-options": "nosniff", 24 | "x-ec2-instance-id": "i-f737987d", 25 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 26 | "vary": "Cookie", 27 | "x-cache": "Miss from cloudfront", 28 | "via": "1.1 b1e9642a58f783f714c47d1a096accf6.cloudfront.net (CloudFront)", 29 | "x-amz-cf-id": "bz5cnAhgVaMMo8KCzf7L-HfgmhbyUToKitnuP1no1x5zcrEGi7HrLQ==" 30 | } 31 | } 32 | ] -------------------------------------------------------------------------------- /tests/fixtures/responses/taxonomyTermsByIndex: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "scope": "https://demo.wp-api.org:443", 4 | "method": "GET", 5 | "path": "/wp-json/wp/v2/categories?page=1&per_page=1", 6 | "body": "", 7 | "status": 200, 8 | "response": [ 9 | "1f8b0800000000000003a552cb4ec33010fc973d87bae56809f1159ceaa832c992ba38f6ca0f4288f2efacd3b40255e2506ea3b1773c9ed9fd04a605b9aba0f1d925465bc62dc626184ac63b90001558e3de191d53a2289550a2c5de6f067ad064363e744a343a61e7c3a844762b365fd82ac1d34ef7c8d32f3f0f988e36774cffbacf74d29fdef97ee4a38b28b3a403167fdb0a0ec54d04394144fb06723fc1312083bffcb1d753f44e898194f878bc1a361895d8c15c9704acc5e6fce9ff6a2e82fad567b67c9fd61ac3e2ef9a43b13990241fd3218dc4a9de275e04e2f3a5278c4f6b0239f07b8be8dad9409cfd4dbaa5f5e1dcfb14d0cea535ecc9f20ef032a59071aee7b9fe06ae5ce1d25d020000" 10 | ], 11 | "headers": { 12 | "content-type": "application/json; charset=UTF-8", 13 | "content-length": "273", 14 | "connection": "close", 15 | "access-control-allow-headers": "Authorization", 16 | "access-control-expose-headers": "X-WP-Total, X-WP-TotalPages", 17 | "allow": "GET", 18 | "content-encoding": "gzip", 19 | "date": "Wed, 15 Jun 2016 03:30:48 GMT", 20 | "server": "nginx/1.4.6 (Ubuntu)", 21 | "x-batcache": "MISS", 22 | "x-cached": "MISS", 23 | "x-content-type-options": "nosniff", 24 | "x-ec2-instance-id": "i-f737987d", 25 | "x-powered-by": "PHP/7.0.4-6+deb.sury.org~trusty+3", 26 | "x-wp-total": "1", 27 | "x-wp-totalpages": "1", 28 | "vary": "Cookie", 29 | "x-cache": "Miss from cloudfront", 30 | "via": "1.1 522fd7e3012339aa909d37522f21bc35.cloudfront.net (CloudFront)", 31 | "x-amz-cf-id": "AfjNRWPyQM2wh937_mq-Hpaqq6QjgxKgTLlr3-nTMQ0xrNUuT47NTA==" 32 | } 33 | } 34 | ] -------------------------------------------------------------------------------- /tests/wordpressRouter.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | var sinon = require('sinon'); 3 | var sinonChai = require('sinon-chai'); 4 | var expect = chai.expect; 5 | chai.use(sinonChai); 6 | 7 | var nock = require('nock'); 8 | var nockBack = require('nock').back; 9 | nockBack.setMode('record'); 10 | nockBack.fixtures = __dirname + '/fixtures/responses'; 11 | 12 | var wordpressRouter = require('../lib/wordpressRouter'); 13 | var falcor = require('falcor'); 14 | var endpoint = 'https://demo.wp-api.org/wp-json'; 15 | 16 | 17 | describe( 'wordpress router', function() { 18 | 19 | var router, model, info, error; 20 | var savedIds = {posts:[], termSlug:null}; 21 | 22 | beforeEach(function() { 23 | router = new wordpressRouter(endpoint); 24 | info = sinon.spy(); 25 | error = sinon.spy(); 26 | router.log = {info: info, error: error}; 27 | model = new falcor.Model({source: router}); 28 | }); 29 | 30 | it( 'imports correctly as a class', function() { 31 | expect(wordpressRouter).to.be.a( 'function' ); 32 | expect(router).to.be.an( 'object' ); 33 | }); 34 | 35 | it( 'can be used as a model source', function() { 36 | expect(model).to.be.an( 'object' ); 37 | }); 38 | 39 | describe( 'recentPosts', function() { 40 | 41 | it( 'retrieves recents posts by indices', function(done) { 42 | nockBack('recentPostsBasic', function(nockDone) { 43 | model.get('recentPosts[0..3].id').subscribe( function(data) { 44 | check(done, function() { 45 | 46 | expect(error.notCalled).to.be.true; 47 | expect(data.json).to.have.key('recentPosts'); 48 | 49 | ['0','1','2','3'].forEach(function (index) { 50 | expect(data.json.recentPosts).to.have.property(index); 51 | var post = data.json.recentPosts[index]; 52 | expect(post).to.have.property('id'); 53 | savedIds.posts.push(post.id); 54 | }); 55 | 56 | nockDone(); 57 | }); 58 | }); 59 | }); 60 | }); 61 | 62 | }); 63 | 64 | describe( 'postsById', function() { 65 | 66 | it( 'requests a single post by id', function(done) { 67 | var postId = savedIds.posts[0]; 68 | 69 | nockBack('postsByIdSingle', function(nockDone) { 70 | model.get('postsById[' + postId + '].id').subscribe( function(data) { 71 | check(done, function() { 72 | 73 | expect(error.notCalled).to.be.true; 74 | 75 | expect(data.json).to.have.key('postsById'); 76 | expect(data.json.postsById).to.have.key('' + postId); 77 | expect(data.json.postsById[postId].id).to.equal(postId); 78 | 79 | nockDone(); 80 | }); 81 | }); 82 | }); 83 | }); 84 | 85 | it( 'requests multiple posts by id with keyed properties', function(done) { 86 | var postIds = savedIds.posts.slice(0,2); 87 | 88 | nockBack('postsByIdMultiple', function(nockDone) { 89 | model.get('postsById[' + postIds.join(',') + ']["id","title","slug"]').subscribe( function(data) { 90 | check(done, function() { 91 | 92 | expect(error.notCalled).to.be.true; 93 | 94 | postIds.forEach(function (postId) { 95 | expect(data.json.postsById).to.have.property('' + postId); 96 | var post = data.json.postsById[postId]; 97 | expect(post).to.have.keys('id','title','slug'); 98 | expect(post.id).to.equal(postId); 99 | }); 100 | 101 | nockDone(); 102 | }); 103 | }); 104 | }); 105 | }); 106 | 107 | }); 108 | 109 | describe( 'taxonomies', function() { 110 | 111 | it( 'fetches metadata for a taxonomy', function(done) { 112 | nockBack('taxonomiesMeta', function(nockDone) { 113 | model.get('taxonomies.category.meta["name","slug"]').subscribe( function(data) { 114 | check(done, function() { 115 | 116 | expect(error.notCalled).to.be.true; 117 | expect(data.json).to.deep.equal({ 118 | "taxonomies": { 119 | "category": { 120 | "meta": { 121 | "name": "Categories", 122 | "slug": "category" 123 | } 124 | } 125 | } 126 | }) 127 | 128 | nockDone(); 129 | }); 130 | }); 131 | }); 132 | }); 133 | 134 | it( 'fetches terms for a taxonomy by index', function(done) { 135 | nockBack('taxonomyTermsByIndex', function(nockDone) { 136 | model.get('taxonomies.categories.terms[0]["name","slug"]').subscribe( function(data) { 137 | check(done, function() { 138 | 139 | expect(error.notCalled).to.be.true; 140 | expect(data.json).to.have.deep.property('taxonomies.categories.terms'); 141 | expect(data.json.taxonomies.categories.terms[0]).to.have.keys('slug','name') 142 | savedIds.termSlug = data.json.taxonomies.categories.terms[0].slug; 143 | 144 | nockDone(); 145 | }); 146 | }); 147 | }); 148 | }); 149 | 150 | it( 'fetches posts by a specified term slug', function(done) { 151 | nockBack('postsByTermSlug', function(nockDone) { 152 | var slug = savedIds.termSlug; 153 | 154 | model.get('postsByTerm.categories.' + slug + '[0..3]["title","id"]').subscribe( function(data) { 155 | check(done, function() { 156 | 157 | expect(error.notCalled).to.be.true; 158 | expect(data.json).to.have.deep.property('postsByTerm.categories.' + slug); 159 | var postList = data.json.postsByTerm.categories[slug]; 160 | 161 | ['0','1','2','3'].forEach(function (index) { 162 | expect(postList).to.have.property(index); 163 | expect(postList[index]).to.have.keys('id','title'); 164 | }); 165 | 166 | nockDone(); 167 | }); 168 | }); 169 | }); 170 | }); 171 | 172 | 173 | 174 | 175 | }); 176 | 177 | describe( 'postsById', function() { 178 | 179 | it( 'requests a single post by id', function(done) { 180 | done(); 181 | }); 182 | }); 183 | 184 | }); 185 | 186 | function check( done, f ) { 187 | try { 188 | f(); 189 | done(); 190 | } catch( e ) { 191 | done( e ); 192 | } 193 | } 194 | --------------------------------------------------------------------------------