├── .gitignore ├── README.md ├── bash ├── bad-request.hyper ├── bash-hyper.hyper ├── bash-hyper.sh ├── bash-hyper.stack ├── error-test.sh ├── exit-if.sh ├── status-pending.hyper ├── test.stack └── url-list.stack ├── conditionals.md ├── docs ├── index.md ├── tips.md └── tweets.md ├── em.stack ├── hyper.config ├── hyper.dump ├── hyper.stack ├── oauth.md ├── package-lock.json ├── package.json ├── plug-ins ├── cj.js ├── fj.js ├── hal.js ├── math.js ├── oauth.js ├── phtal.js ├── siren.js ├── stub.js.test └── wstl.js ├── sample.config ├── scripts ├── activate-display-path.txt ├── basic-auth.txt ├── bookstore-xml.script ├── captialone-api.txt ├── cj-session.txt ├── cj-support.txt ├── cj-template-support.txt ├── config-test.cfg ├── config.txt ├── display-support.txt ├── etag-example.hyper ├── example.txt ├── exit-if.hyper ├── forms-json.local ├── hal-support.txt ├── help.txt ├── http-methods.local ├── jf-support.local ├── local-users │ └── mod-user.txt ├── masto-tag.txt ├── math-plugin.txt ├── mstdn-write.txt ├── oauth-support-safe.local ├── omdb-api-testing.hyper ├── queries-support.local ├── request-all.hyper ├── shell-test.txt ├── show-request.hyper ├── siren-delete-ops.txt ├── siren-stack-path.txt ├── siren-support.txt ├── siren-testing.txt ├── siren-update.txt ├── siren-user.txt ├── soap-support.script ├── stack-data.txt ├── store-book-json-path.txt ├── store-book-xml-path.txt ├── swapi-testing.hyper ├── test-all.sh ├── twitter-post.txt ├── vars-support.txt ├── with-form.txt ├── with-rel.txt └── wstl-rel.local ├── src ├── config.js ├── display.js ├── hyper-utils.js ├── index.js └── stack.js ├── user-list-stack.dump └── working └── str.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | node_modules/ 23 | 24 | # Logs and databases # 25 | ###################### 26 | *.log 27 | *.sql 28 | *.sqlite 29 | 30 | # OS generated files # 31 | ###################### 32 | .DS_Store 33 | .DS_Store? 34 | ._* 35 | .Spotlight-V100 36 | .Trashes 37 | ehthumbs.db 38 | Thumbs.db 39 | *~ 40 | 41 | # local environment data # 42 | ########################## 43 | *.postman_environment.json 44 | *.env 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # **hyper** : Interactive Hypermedia Shell 5 | 6 | _Exploring an interactive REPL/shell for interacting with HTTP-based hypermedia services_ 7 | 8 | ## Summary 9 | The **hyper** utility is a simple command-line style shell/REPL for interacting with an online services/APIs. While a fully-functional HTTP client, **hyper** is especially good at dealing with hypermedia services including [Collection+JSON](http://amundsen.com/media-types/collection/), [SIREN](https://github.com/kevinswiber/siren), and [HAL](https://datatracker.ietf.org/doc/html/draft-kelly-json-hal-08). There are plans to add support for [PRAG+JSON](https://mamund.github.io/prag-json/), [MASH+JSON](https://mamund.github.io/mash-json), and possibly [UBER](http://uberhypermedia.com/) in the future. 10 | 11 | Along with HTTP- and mediatype-aware commands, **hyper** also supports some convience functionality like SHELL commands, configuration file management, and a LIFO stack to handle local memory variabes. 12 | 13 | Importantly, **hyper** is not just a shell/REPL, it is a hypermedia DSL. It encourages users to `think' in hypermedia. Rather than writing complex HTTP queries that look like this (an example that works fine in **hyper**): 14 | 15 | ``` 16 | ACTIVATE http://localhost:8181/task/ 17 | WITH-METHOD PUT 18 | WITH-BODY title=testing&tags=hyper&completeFlag=false 19 | WITH-ENCODING application/x-www-form-urlencoded 20 | WITH-HEADERS {"if-none-match":"*"} 21 | ``` 22 | 23 | The **hyper** shell can also use mediatype-aware convience commands to locate, parse, fill, and execute inline hypermedia controls. This results in a much more readable **hyper** exeperience: 24 | 25 | ``` 26 | STACK PUSH { 27 | "title":"testing", 28 | "tags":"hyper", 29 | "completeFlag":"false" 30 | } 31 | 32 | ACTIVATE http://locahost:8181/home/ 33 | ACTIVATE WITH-FORM taskFormAdd WITH-STACK 34 | ``` 35 | 36 | In both cases, the same work is completed. In the first example, a human can read all the docs and examples and craft a successful HTTP PUT request. This works until the server changes a parameter (e.g. moves from PUT to POST). 37 | 38 | In the second example, the **hyper** engine loads available data (it could have been from disk using `STACK LOAD task-record.txt`) and uses identified hypermedia controls (in this case the `taskFormAdd` control) to complete the request. This will continue to work even if HTTP details are changed (like changing PUT to POST)-- as long as the hypermedia form `taskFormAdd` is included in the response. 39 | 40 | ## Motivation 41 | The idea for this shell comes from other REPL-style interactive CLIs like `node` and command-line tools like `curl`. You can start a stateful client session by typing `hyper` at the command line. Then you can make an HTTP request (`ACTIVATE`) and manipulate the responses. You can also write hyper commands in a file and pipe this file into hyper for a scripted experience: (`hyper < scripts/sample.txt > scripts/sample.log`). 42 | 43 | **Hyper** is "mediatype-aware" -- that is, it recognizes well-known media types and offers convience methods for dealing with them. For example after loading a SIREN response, you can use the following commands: 44 | 45 | ``` 46 | # SIREN example 47 | GOTO http://rwcbook10.herokuapp.com 48 | SIREN LINKS 49 | SIREN ENTITIES 50 | SIREN ACTIONS 51 | 52 | GOTO WITH-REL taskFormListByUser WITH-QUERY {"assignedUser" : "alice"} 53 | ``` 54 | 55 | That last command 1) uses the `href` associated with the SIREN action element identified by the `rel:taskFormListByUser`, 2) supplies a querystring argument and 3) makes the HTTP request. 56 | 57 | Another way to use **hyper** is to load the data stack with some name/value pairs and then use a named form within the response to execute an action. Like this: 58 | 59 | ``` 60 | # read list 61 | GOTO http://rwcbook10.herokuapp.com 62 | 63 | # add data to the stack and execute the write operation 64 | STACK PUSH {"title":"just another one","tags":"with-test","completeFlag":"false"} 65 | GOTO WITH-FORM taskFormAdd WITH-STACK 66 | 67 | # check the write results using the same stack data 68 | GOTO WITH-FORM taskFormListByTag WITH-STACK 69 | SIREN PATH $..*[?(@property==='tags'&&@.match(/with-test/i))]^ 70 | ``` 71 | 72 |

NOTE: Spaces are significant in HYPER commands. The above example shows spaces are properly handed within embeded quotes (STACK PUSH {"title":"just another one","tags":"with-test","completeFlag":"false"}). However, there must be no spaces between elements in the JSON segments.

73 | 74 | Note that the client will use whatever URL, HTTP method, and body encoding the server indicates. Also, notice that the client will automatically match up any form fields on the stack to fill in the form. Even when the server changes details (new URL, different method, etc.), the client will be able to handle the write operation without changes. 75 | 76 | 77 | You can also use JSONPath to query responses: 78 | 79 | ``` 80 | SIREN PATH $.entities.*[?(@property==='id'&&@.match(/rmqzgqfq3d/i))]^.[id,title,href,type] 81 | ``` 82 | You can also use **hyper** to program a modificaiton of existing records: 83 | 84 | ``` 85 | REQUEST WITH-URL http://rwcbook10.herokuapp.com WITH-ACCEPT application/vnd.siren+json 86 | REQUEST WITH-PATH $.entities[0].href WITH-ACCEPT application/vnd.siren+json 87 | STACK PUSH WITH-PATH $.properties 88 | STACK SET {"tags":"fishing,skiing,hiking"} 89 | REQUEST WITH-FORM taskFormEdit WITH-STACK WITH-ACCEPT application/vnd.siren+json 90 | EXIT 91 | 92 | ``` 93 | 94 | In the above example, the **hyper** : 95 | 96 | * Calls the root resource of the serivce asking for a SIREN formatted response 97 | * Locates the first item in the collection, pulls its HREF value and calls that record 98 | * Pushes the item properties of the record onto the local stack 99 | * Updates the `tags` value on the stack to reflect the change 100 | * Uses the `taskFormEdit` form, fills it with values from the stack and makes the request 101 | * Once all is done, the script exits 102 | 103 | Similar options exist for HAL, CollectionJSON, JSON+FORMS, and other formats. These various format types are defined using external plug-ins that can be created and just dropped into the `/plugins/` folder to be loaded at runtime. 104 | 105 | ## Examples 106 | See the [scripts](scripts/) folder for lots of working examples. 107 | 108 | ## Futures 109 | Some notes on future enhancements 110 | 111 | * [Conditionals](conditionals.md) 112 | 113 | ## Feature tracking 114 | This is a work in progress and totally unstable/unreliable. Here the current workplan and status for this project: 115 | 116 | - [x] : Initial CLI loop 117 | - [x] : support for piped scripts (in and out) 118 | - [x] : support for **#** - comment lines 119 | - [x] : support for **VERSION** - returns version info 120 | - [x] : support for **EXIT|STOP** - halt and exit with 0 121 | - [ ] : support for **EXIT-ERR** - halt and exit with 1 122 | - [x] : support for **EXIT-IF** - halt and exit with 1 if simple condition is met 123 | - [x] : support for .. INVALID-URL __ : returns TRUE if the string is NOT a valid URL 124 | - [x] : support for .. STACK-EMPTY : returns TRUE if there is nothing on the internal stack 125 | - [x] : support for **CLEAR** - clears the console 126 | - [x] : support for **SHELL** simple SHELL (bash/dos) support 127 | - [x] : support for .. LS|DIR _[folder/path]_ 128 | - [x] : support for **PLUGINS** returns list of loaded external plug-ins 129 | - [x] : support for **CONFIG** (READ) returns NVP of saved config data 130 | - [x] : support for .. FILE|LOAD _[filename]_ loads config file (defaults to "hyper.config") 131 | - [x] : support for .. SAVE|WRITE _[filename]_ loads config file (defaults to "hyper.config") 132 | - [x] : support for .. SET _<{n:v,...}>_ shared config file write 133 | - [x] : support for .. CLEAR removes all settings 134 | - [x] : support for .. RESET resets to default settings 135 | - [x] : support for .. REMOVE __ removes the named item 136 | - [x] : support for **STACK** JSON object LIFO stack 137 | - [x] : support for .. CLEAR|FLUSH clears all the items from the stack 138 | - [x] : support for .. PEEK displays the JSON object at the stop of the stack 139 | - [x] : support for .. PUSH _<{n:v,...}>_ adds a new JSON object to the stack 140 | - [x] : support for .. PUSH WITH-REPONSE adds a new item on the stack from the top of the _response_ stack 141 | - [x] : support for .. PUSH WITH-PATH __ adds a new item on the stack which is the result of the JSONPATH 142 | - [x] : support for .. EXPAND-ARRAY expands the array on the top of the stack into n-items on the stack. 143 | - [x] : support for .. POP removes the top item from the stack 144 | - [x] : support for .. LEN|LENGTH returns depth of the stack 145 | - [x] : support for .. SET _<{"n":"v",...}>_ update the JSON object on the top of the stack 146 | - [x] : support for .. LOAD|FILE _[filename]_ reads a single JSON object from disk onto the stack (defaults to hyper.stack) 147 | - [x] : support for .. SAVE|WRITE _[filename]_ writes the top item on the stack to disk (defaults to hyper.stack) 148 | - [x] : support for .. DUMP _[filename]_ writes the full stack to disk (defaults to hyper.dump) 149 | - [x] : support for .. FILL _[filename]_ replaces the current stack with contents in disk file (defaults to hyper.dump) 150 | - [x] : support for **OAUTH** OAuth 2.0 support 151 | - [x] : support for .. LOAD _[filename]_ loads OAuth config file (defaults to "oauth.env") 152 | - [x] : support for .. SAVE _[filename]_ loads OAuth config file (defaults to "oauth.env") 153 | - [x] : support for .. DEFINE __ _<{n:v,...}>_ Creates an entry in the OAuth configuration 154 | - [x] : support for .. UPDATE __ _<{n:v,...}>_ Modifies settings in an existing OAuth configuration 155 | - [x] : support for .. GENERATE|GEN __ Generates an access token using the configuration data 156 | - [x] : support for .. REMOVE __ removes the named item 157 | - [x] : support for **ACTIVATE**|CALL|GOTO|GO - makes an HTTP request 158 | - [x] : support for .. WITH-URL __ - uses URL to make the request 159 | - [x] : support for .. WITH-REL __ - uses HREF value on the associated in-doc element 160 | - [x] : support for .. WITH-ID __ - uses HREF value on the associated in-doc element 161 | - [x] : support for .. WITH-NAME __ - uses HREF value on the associated in-doc element 162 | - [x] : support for .. WITH-PATH __ - uses value from JSONPath result as the URL 163 | - [x] : support for .. WITH-ACCEPT _string|$#>_ - sets the accept header directly 164 | - [x] : support for .. WITH-HEADERS _<{n:v,...}|$#>_ - request headers 165 | - [x] : support for .. WITH-OAUTH __ - Sets the `authorization` header using the named OAUTH token 166 | - [x] : support for .. WITH-BASIC __ - Sets the `authorization` header using the named BASIC config 167 | - [x] : support for .. WITH-QUERY _<{n:v,...}|$#>_ - query string args as JSON nvps 168 | - [x] : support for .. WITH-BODY __ - for POST/PUT/PATCH (defaults to app/form-urlencoded) 169 | - [x] : support for .. WITH-METHOD __ - to set HTTP method (defaults to GET) 170 | - [x] : support for .. WITH-ENCODING __ - to set custom encoding for POST/PUT/PATCH 171 | - [x] : support for .. WITH-FORMAT - sets `accept` header w/ config value 172 | - [x] : support for .. WITH-PROFILE - sets `link` profile header w/ config value 173 | - [x] : support for .. WITH-FORM __ - uses the metadata of the named form (URL, METHOD, ENCODING, FIELDS) to construct an HTTP request (SIREN-ONLY) 174 | - [x] : support for .. WITH-STACK - uses the top level STACK item as a set of vars for other operations (e.g. to fill in forms, supply querystring values, headers, etc. 175 | - [x] : support for .. WITH-DATA __ - for use fill in forms with ad-hoc data 176 | - [x] : support for **DISPLAY**|SHOW - show saved reponse (from top of the LIFO stack) 177 | - [x] : support for .. ALL - returns the complete interaction (request, response metadata, response body) 178 | - [x] : support for .. REQUEST - returns request info (URL, method, querystring, body, headers) 179 | - [x] : support for .. METADATA|META - returns the response metadata (URL, status, & headers) 180 | - [x] : support for .. RESPONSE|PEEK - returns response body from the top of the stack 181 | - [x] : support for .. URL|HREF - returns actual URL of the response 182 | - [x] : support for .. STATUS|STATUS-CODE - returns HTTP status of the response 183 | - [x] : support for .. CONTENT-TYPE - returns HTTP content-type of the response 184 | - [x] : support for .. HEADERS - returns the complete HTTP header collection of the response 185 | - [x] : support for .. POP remove response from top of the stack 186 | - [x] : support for .. CLEAR|FLUSH - remove all responses from the stack 187 | - [x] : support for .. LEN|LENGTH - returns length of saved stack 188 | - [x] : support for .. PATH __ returns results of a query (xml or json)from top-of-stack response 189 | - [x] : support for .. JPATH __ returns results of an JSONPath query from top-of-stack response 190 | - [x] : support for .. XPATH __ returns results of an XPath query from top-of-stack response 191 | - [x] : support for **CJ** returns a strong-typed version of response from top of the stack (`vnd.collection+json`) 192 | - [x] : support for .. METADATA returns metadata array from a collection+JSON response 193 | - [x] : support for .. LINKS returns links array from a collection+JSON response 194 | - [x] : support for .. ITEMS returns items array from a collection+JSON response 195 | - [x] : support for .. QUERIES returns queries array from a collection+JSON response 196 | - [x] : support for .. TEMPLATE returns template collection from a collection+JSON response 197 | - [x] : support for .. ERROR|ERRORS returns error object from a collection+JSON response 198 | - [x] : support for .. RELATED returns the related object from a collection+JSON response 199 | - [x] : support for .. ID|NAME|REL|TAG __ returns a single node 200 | - [ ] : support for .. IDS|RELS|NAMES|TAGS returns a simple list 201 | - [x] : support for .. PATH __ returns results of a JSONPath query from a collection+JSON response 202 | - [x] : support for **HAL** returns a strong-typed version of response from top of the stack (`vnd.hal+json`) 203 | - [x] : support for .. LINKS returns links array from a HAL response 204 | - [x] : support for .. EMBEDDED returns items array from a HAL response 205 | - [x] : support for .. ID|REL|KEY|NAME|TAG __ returns a single node 206 | - [ ] : support for .. IDS|RELS|KEYSTAGS returns a simple list 207 | - [x] : support for .. PATH __ returns results of a JSONPath query from a HAL response 208 | - [x] : support for **SIREN** returns a strong-typed version of response from top of the stack (`vnd.siren+json`) 209 | - [x] : support for .. LINKS returns links array from a SIREN response 210 | - [x] : support for .. ACTIONS|FORMS returns actions array from a SIREN response 211 | - [x] : support for .. ENTITIES returns entities array from a SIREN response 212 | - [x] : support for .. PROPERTIES returns properties array from a SIREN response 213 | - [x] : support for .. TAG|CLASS __ returns nodes associated with the CLASS value 214 | - [x] : support for .. ID|ENTITY __ returns an entity associated with the ID 215 | - [x] : support for .. REL|LINK __ returns a link associated with the REL 216 | - [x] : support for .. NAME|FORM|ACTION __ returns an action associated with the NAME 217 | - [ ] : support for .. IDS|RELS|NAMES|FORMS|TAGS|CLASSES returns a simple list 218 | - [x] : support for .. PATH __ returns results of a JSONPath query from a SIREN response 219 | - [x] : support for **WSTL** returns a strong-typed version of response from top of the stack (`vnd.wstl+json`) 220 | - [x] : support for .. TITLE returns title string from a WSTL response 221 | - [x] : support for .. ACTIONS returns actions array from a WSTL response 222 | - [x] : support for .. DATA returns entities array from a WSTL response 223 | - [x] : support for .. RELATED returns related object from a WSTL response 224 | - [x] : support for .. CONTENT returns content object from a WSTL response 225 | - [x] : support for .. ID|REL|NAME|FORM|TAG|TARGET __ returns a single node 226 | - [ ] : support for .. IDS|RELS|NAMES|FORMS|TAGS|TARGETS returns a simple list 227 | - [x] : support for .. PATH __ returns results of a JSONPath query from a WSTL response 228 | - [x] : support for **FJ** returns a strong-typed version of JSON+FORMS response from top of the stack (`forms+json`) 229 | - [x] : support for .. METADATA returns metadata array from a response 230 | - [x] : support for .. LINKS returns links array from a response 231 | - [x] : support for .. ITEMS returns items array from a response 232 | - [x] : support for .. ID __ returns an element (metadata, link, item) associated with the ID 233 | - [x] : support for .. TAG __ returns matching nodes 234 | - [x] : support for .. REL __ returns a link associated with the REL 235 | - [x] : support for .. NAME __ returns an element (metadata, link, property) associated with the NAME 236 | - [x] : support for .. IDS|NAMES|RELS|FORMS|TAGS returns a simple list 237 | - [x] : support for .. PATH __ returns results of a JSONPath query from a response 238 | - [ ] : support for **MASH** returns a strong-typed version of response from top of the stack (`vnd.mash+json`) 239 | - [ ] : support for .. METADATA returns metadata array from a response 240 | - [ ] : support for .. LINKS returns links array from a response 241 | - [ ] : support for .. ITEMS returns items array from a response 242 | - [ ] : support for .. TAG __ returns matching nodes 243 | - [ ] : support for .. ID __ returns an element (metadata, link, item) associated with the ID 244 | - [ ] : support for .. REL __ returns a link associated with the REL 245 | - [ ] : support for .. NAME __ returns an element (metadata, link, property) associated with the NAME 246 | - [ ] : support for .. IDS|NAMES|RELS|FORMS|TAGS returns a simple list 247 | - [ ] : support for .. PATH __ returns results of a JSONPath query from a SIREN response 248 | - [ ] : support for **PRAG** returns a strong-typed version of response from top of the stack (`vnd.prag+json`) 249 | - [ ] : support for .. METADATA returns metadata array from a PRAG response 250 | - [ ] : support for .. LINKS returns links array from a PRAG response 251 | - [ ] : support for .. ITEMS returns items array from a PRAG response 252 | - [ ] : support for .. ID __ returns an element (metadata, link, item) associated with the ID 253 | - [ ] : support for .. TAG __ returns matching nodes 254 | - [ ] : support for .. REL __ returns a link associated with the REL 255 | - [ ] : support for .. NAME __ returns an element (metadata, link, property) associated with the NAME 256 | - [ ] : support for .. IDS|NAMES|RELS|FORMS|TAGS returns a simple list 257 | - [ ] : support for .. PATH __ returns results of a JSONPath query from a SIREN response 258 | 259 | ## TODO Items 260 | Here's a list of things I think need to be done before #HyperLang (as I like to call it) is "complete": 261 | 262 | - [ ] : improved support for VERBOSE setting, possibly levels. See noting (0), see status (1), see errors (2) 263 | - [ ] : support for URITemplates - required for HAL (and other formats?) 264 | - [ ] : support for HAL-FORMS - could greatly enhance HAL support 265 | - [ ] : support for IF-ERROR - error checking (`4xx`, `5xx`) 266 | - [ ] : support for JUMP _{label}_ - jump to defined label in the script (might be forward-only jumping) 267 | - [ ] : **Loops** : Do we really want to implement loops? If yes, then it MUST be a single line element. Like list comprehensions in Python. For example `WHILE STACK NOT EMPTY ACTIVATE WITH-FORM taskAddForm WITH-STACK POP`. 268 | - [ ] : **Branching** : Would like to avoid branching but might consider `JUMP EXIT` or `JUMP :label|line` (much harder) 269 | 270 | ## Dependencies 271 | These modules are used in the hyper app. 272 | 273 | * https://github.com/ForbesLindesay/sync-request 274 | * https://www.npmjs.com/package/jsonpath-plus 275 | * https://www.npmjs.com/package/stack-lifo 276 | * https://www.npmjs.com/package/html2json 277 | * https://www.npmjs.com/package/glob 278 | * https://www.npmjs.com/package/valid-url 279 | * https://www.npmjs.com/package/xmldom 280 | * https://www.npmjs.com/package/xpath 281 | 282 | ## Plug-ins Support 283 | You an author your own **hyper** plug-in and place it in the `/plugins/` folder of the project. It will be automatically loaded at runtime. See [Plug-In Authoring](plugin-authoring.md) for details. 284 | 285 | ## Source Code 286 | You'll find the source code for this utility in the [src](src/) folder. 287 | 288 | **WARNING**: This is all proof-of-concept code and it's pretty messy. I spend time exploring new features much more than I do properly grooming the source code. If you're offended by this kind of behavior, don't look behind the curtain on this one -- I'll only disappoint you[grin]. -- @mamund 289 | 290 | -------------------------------------------------------------------------------- /bash/bad-request.hyper: -------------------------------------------------------------------------------- 1 | # 2 | # this should result in an error exit 3 | # 4 | 5 | # set exit on 400s & force output 6 | CONFIG SET {"exit400":"true","verbose":"true"} 7 | 8 | # this is an OK request 9 | GOTO WITH-URL https://company-atk.herokuapp.com 10 | 11 | # this is not an OK request 12 | GOTO WITH-URL https://company-atk.herokuapp.com/z-asudf-1 13 | 14 | # EOF 15 | 16 | -------------------------------------------------------------------------------- /bash/bash-hyper.hyper: -------------------------------------------------------------------------------- 1 | # 2 | # mixing bash and hyper 3 | # 4 | 5 | # collect any recs at pending status 6 | GOTO WITH-URL https://company-atk.herokuapp.com 7 | GOTO WITH-NAME list 8 | GOTO WITH-FORM filter WITH-DATA {"status":"pending"} 9 | 10 | # write urls of pending recs to disk 11 | STACK CLEAR 12 | STACK PUSH WITH-PATH $..items..links.*[?(@property==='name'&&@.match(/read/i))]^.href 13 | STACK EXPAND-ARRAY href 14 | STACK DUMP url-list.stack 15 | 16 | # report total recs to process 17 | STACK PUSH WITH-PATH $..items.length 18 | STACK WRITE bash-hyper.stack 19 | STACK POP 20 | 21 | EXIT 22 | 23 | # EOF 24 | 25 | -------------------------------------------------------------------------------- /bash/bash-hyper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # test hyper output values 4 | 5 | # set local vars 6 | bash_script="./bash-hyper.sh" 7 | read_script="bash-hyper.hyper" 8 | write_script="status-pending.hyper" 9 | 10 | read_log="bash-hyper.log" 11 | write_log="status-pending.log" 12 | 13 | stack_file="bash-hyper.stack" 14 | 15 | # check for old files 16 | rm -f -- $read_log 17 | rm -f -- $write_log 18 | rm -f -- $stack_file 19 | 20 | # run the script and pull results 21 | echo "Checking for items to process..." 22 | hyper < $read_script > $read_log 23 | items=`cat $stack_file` 24 | 25 | # test to see if we're done 26 | if [ $? -eq 1 ] 27 | then 28 | echo "all done! (ERR)" 29 | exit 30 | fi 31 | 32 | if [ "$items" == "null" ] 33 | then 34 | echo "all done!" 35 | exit 36 | else 37 | echo "$items items found." 38 | echo "more work to do!" 39 | hyper < $write_script > $write_log 40 | bash ./bash-hyper.sh 41 | fi 42 | 43 | # EOF 44 | 45 | -------------------------------------------------------------------------------- /bash/bash-hyper.stack: -------------------------------------------------------------------------------- 1 | null -------------------------------------------------------------------------------- /bash/error-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # testing bash/hyper interactions 4 | 5 | 6 | # call hyper script 7 | hyper < bad-request.hyper 8 | 9 | # check exit status 10 | if [ $? -eq 1 ] 11 | then 12 | echo "*** Bad Request" 13 | fi 14 | 15 | # eof 16 | 17 | -------------------------------------------------------------------------------- /bash/exit-if.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | hyper < ../scripts/exit-if.hyper 4 | if [ $? -eq 1 ] 5 | then 6 | echo ERROR! 7 | fi 8 | 9 | 10 | -------------------------------------------------------------------------------- /bash/status-pending.hyper: -------------------------------------------------------------------------------- 1 | # 2 | # update pending record 3 | # 4 | 5 | # set error level 6 | CONFIG SET {"error400":"true"} 7 | # load work list 8 | STACK FILL url-list.stack 9 | 10 | # is top stack item a URL? 11 | EXIT-IF INVALID-URL ##href## 12 | 13 | # fetxh reord and use form to update 14 | GOTO WITH-URL ##href## 15 | GOTO WITH-FORM status WITH-DATA {"status":"active"} 16 | 17 | # clean up the stack and save 18 | STACK POP 19 | STACK DUMP url-list.stack 20 | 21 | EXIT 22 | 23 | # EOF 24 | -------------------------------------------------------------------------------- /bash/test.stack: -------------------------------------------------------------------------------- 1 | { 2 | "you": "yours" 3 | } -------------------------------------------------------------------------------- /bash/url-list.stack: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "href": null 4 | } 5 | ] -------------------------------------------------------------------------------- /conditionals.md: -------------------------------------------------------------------------------- 1 | ## Conditionals 2 | 3 | _Notes on adding conditional support for #HyperLang and #HyperCLI_ 4 | 5 | ``` 6 | IF 7 | 8 | IF 9 | 10 | 11 | - EQ 12 | - NEQ 13 | - GT 14 | - NGT 15 | - LT 16 | - NLT 17 | - GTE 18 | - NGTE 19 | - LTE 20 | - NLTE 21 | - CONTAINS 22 | - NOT-CONTAINS 23 | - EXISTS 24 | - NOT-EXISTS 25 | 26 | 27 | - JUMP-TO 28 | - EXIT 29 | - EXIT-ERR 30 | 31 | CONTAINS RESPONSE 32 | CONTAINS PATH 33 | ``` 34 | 35 | ### Use Cases, Examples 36 | 37 | ``` 38 | ACTIVATE http://locahost:8181/task 39 | IF CONTAINS WITH-PATH $.collection.links user JUMP-TO :process-users 40 | EXIT-ERR 41 | 42 | ACTIVATE http://locahost:8181/task 43 | IF GTE STATUS 400 EXIT-ERR Task\.\call\.\failed 44 | 45 | CALL WITH-FORM createUserForm WITH-STACK 46 | IF LT STATUS 400 JUMP-TO :next-step 47 | EXIT-ERR createUserForm\.\failed. 48 | :next-step 49 | IF NOT-EXISTS REL home EXIT-ERR missing\.\home\.\link 50 | GOTO WITH-REL home 51 | 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | _The home of HyperCLI and HyperLANG_ 2 | 3 |

4 | 5 | ### Links 6 | 7 | * [**NPM Project**](https://www.npmjs.com/package/@mamund/hyper) 8 | * [**README**](https://github.com/webapicookbook/hyper#readme) 9 | * [**Github Project**](https://github.com/webapicookbook/hyper) 10 | * [**Tweets**](tweets.md) 11 | * [**Quick Tips**](tips.md) 12 | 13 | ### About 14 | The **hyper** utility is a simple command-line style shell/REPL for interacting with an online services/APIs. While a fully-functional HTTP client, **hyper** is especially good at dealing with hypermedia services including Collection+JSON, SIREN, and HAL. There are plans to add support for PRAG+JSON, MASH+JSON, and possibly UBER in the future. 15 | 16 | Along with HTTP- and mediatype-aware commands, **hyper** also supports some convience functionality like SHELL commands, configuration file management, and a LIFO stack to handle local memory variabes. 17 | 18 | The idea for this shell comes from other REPL-style interactive CLIs like node and command-line tools like curl. You can start a stateful client session by typing **hyper** at the command line. Then you can make an HTTP request (ACTIVATE) and manipulate the responses. You can also write **hyper** commands in a file and pipe this file into **hyper** for a scripted experience: (`hyper < scripts/sample.txt > scripts/sample.log`). 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/tips.md: -------------------------------------------------------------------------------- 1 | ## Quick Tips 2 | 3 | _Simple tips and tricks to get the most out of **HyperCLI** and **HyperLANG**_ 4 | 5 | * [The SIREN's Call](https://webapicookbook.github.io/hyper/tips.html#the-sirens-call) 6 | * [XPath Marks the Spot](https://webapicookbook.github.io/hyper/tips.html#xpath-marks-the-spot) 7 | * [Try Using SOAP Next Time](https://webapicookbook.github.io/hyper/tips.html#try-using-soap-next-time) 8 | * [Hello, Hyper!](https://webapicookbook.github.io/hyper/tips.html#hello-hyper) 9 | * [It Varies](https://webapicookbook.github.io/hyper/tips.html#it-varies) 10 | * [Gimme Some Space, Dude](https://webapicookbook.github.io/hyper/tips.html#gimme-some-space-dude) 11 | 12 | 13 | ### The SIREN's Call 14 | One of the features of **HyperCLI** is the ability to write custom plug-ins to expand the **HyperLANG** and increase support for additional formats. One of the first plug-ins created for **HyperCLI** is one that supports the [SIREN](https://github.com/kevinswiber/siren#siren-a-hypermedia-specification-for-representing-entities) hypermedia format. 15 | 16 | You can use the `PLUGINS` command to confirm that your install of **HyperCLI** includes support for SIREN. The current default install has the following plugins: 17 | 18 | ``` 19 | > PLUGINS 20 | ["CJ","FJ","HAL","OAUTH","SIREN","WSTL"] 21 | ``` 22 | 23 | You can pull up a list of SIREN commands like this: 24 | 25 | ``` 26 | > SIREN HELP 27 | SIREN 28 | LINKS 29 | ENTITIES 30 | ACTIONS|FORMS 31 | PROPERTIES 32 | IDS|RELS|NAMES|FORMS|TAGS|CLASSES (returns simple list) 33 | TAG|CLASS returns matching nodes 34 | ID|ENTITY (for Entities) 35 | REL|LINK (for Links) 36 | NAME|FORM|ACTION (for Actions) 37 | PATH 38 | ``` 39 | 40 | Let's explore SIREN support in **HyperCLI**. First, we'll make a simple HTTP request to a service that returns SIREN responses: 41 | 42 | ``` 43 | # make initial request 44 | > REQUEST WITH-URL http://rwcbook10.herokuapp.com 45 | STATUS 200 46 | https://rwcbook10.herokuapp.com/task/ 47 | application/vnd.siren+json 48 | ``` 49 | 50 | Now, we can use common SIREN commands to inspect the response. Try the following commands and see what **HyperCLI** returns: 51 | 52 | ``` 53 | SIREN LINKS 54 | SIREN ENTITIES 55 | SIREN ACTIONS 56 | SIREN PROPERTIES 57 | ``` 58 | 59 | Next, let's inspect the first entity in the list returned by the service: 60 | 61 | ``` 62 | > REQUEST WITH-PATH $.entities[0].href 63 | STATUS 200 64 | http://rwcbook10.herokuapp.com/task/1l9fz7bhaho 65 | application/vnd.siren+json 66 | > SHOW REQUEST 67 | { 68 | "url": "http://rwcbook10.herokuapp.com/task/1l9fz7bhaho", 69 | "method": "GET", 70 | "query": {}, 71 | "headers": { 72 | "user-agent": "hyper-cli" 73 | }, 74 | "body" 75 | } 76 | ``` 77 | 78 | Note that we didn't type in a URL. We used the hypermedia information in the SIREN response instead. 79 | 80 | We can also use the `STACK` command in **HyperLANG** to store parts of the response into local memory for later use. 81 | 82 | ``` 83 | STACK PUSH WITH-PATH $.properties 84 | { 85 | "content": "

Manage your TPS Tasks here.

You can do the following:

  • Add, Edit and Delete tasks
  • Mark tasks \"complete\", assign tasks to a user
  • Filter the list by Title, Assigned User, and Completed Status
", 86 | "id": "1l9fz7bhaho", 87 | "title": "extension", 88 | "tags": "fishing, skiing, hiking", 89 | "completeFlag": "false", 90 | "assignedUser": "bob", 91 | "dateCreated": "2016-02-01T01:08:15.205Z", 92 | "dateUpdated": "2021-09-15T23:24:48.933Z" 93 | } 94 | ``` 95 | 96 | Now, we can modify one of the properties in the entry on the top of the `STACK`. Let's update the `tags` value: 97 | ``` 98 | STACK SET {"tags":"fishing, skiing, hiking, spelunking"} 99 | ``` 100 | 101 | One of the cool features of SIREN is that the service can send details on how to modify records on the server. For example, here are the details for updating an entity: 102 | 103 | ``` 104 | > SIREN FORM taskFormEdit 105 | { 106 | "name": "taskFormEdit", 107 | "title": "Edit Task", 108 | "href": "http://rwcbook10.herokuapp.com/task/1l9fz7bhaho", 109 | "type": "application/x-www-form-urlencoded", 110 | "method": "PUT", 111 | "fields": [ 112 | { "name": "id", 113 | "type": "text", 114 | "value": "", 115 | "title": "ID", 116 | "class": ["task"], 117 | "readOnly": true, 118 | "required": false }, 119 | { "name": "title", 120 | "type": "text", 121 | "value": "", 122 | "title": "Title", 123 | "class": ["task"], 124 | "readOnly": false, 125 | "required": false }, 126 | { "name": "tags", 127 | "type": "text", 128 | "value": "", 129 | "title": "Tags", 130 | "class": ["task"], 131 | "readOnly": false, 132 | "required": false }, 133 | { "name": "completeFlag", 134 | "type": "select", 135 | "value": "false", 136 | "title": "Complete", 137 | "class": ["task"], 138 | "readOnly": false, 139 | "required": false, 140 | "pattern": "true|false" } 141 | ] 142 | } 143 | ``` 144 | Now that we have an updated entity on the `STACK` and we know there is a hypermedia control availble for editing records, we can use **HyperLANG** to write that updated entity from the `STACK` back to the service: 145 | 146 | ``` 147 | > REQUEST WITH-FORM taskFormEdit WITH-STACK 148 | STATUS 200 149 | http://rwcbook10.herokuapp.com/task/ 150 | application/vnd.siren+json 151 | > SHOW REQUEST 152 | { 153 | "url": "http://rwcbook10.herokuapp.com/task/1l9fz7bhaho", 154 | "method": "PUT", 155 | "query": {}, 156 | "headers": { 157 | "content-type": "application/x-www-form-urlencoded", 158 | "user-agent": "hyper-cli" 159 | }, 160 | "body": "id=1l9fz7bhaho&title=extension&tags=fishing%2C%20skiing%2C%20hiking%2C%20spelunking&completeFlag=false" 161 | } 162 | ``` 163 | 164 | Again, we didn't use any URL or HTTP method. That information is in the `taskFormEdit` hypermedia control. And we used the entity on the `STACK` so we didn't need to type any body parameters, either. 165 | 166 | Finally, we can confirm the update was completed by inspecting the last response: 167 | 168 | ``` 169 | > SIREN PATH $.entities[0] 170 | $.entities[0] 171 | { 172 | "class": [ 173 | "task" 174 | ], 175 | "href": "//rwcbook10.herokuapp.com/task/1l9fz7bhaho", 176 | "rel": [ 177 | "item" 178 | ], 179 | "type": "application/vnd.siren+json", 180 | "id": "1l9fz7bhaho", 181 | "title": "extension", 182 | "tags": "fishing, skiing, hiking, spelunking", 183 | "completeFlag": "false", 184 | "assignedUser": "bob", 185 | "dateCreated": "2016-02-01T01:08:15.205Z", 186 | "dateUpdated": "2021-09-15T23:42:45.608Z" 187 | } 188 | ``` 189 | 190 | And there you have it. **HyperCLI** has been expanded to support SIREN operations and now we can explore and edit content on any server that supports the SIREN media type. 191 | 192 | ### XPath Marks the Spot 193 | The **HyperCLI** supports basic XPATH queries for XML responses. That means you can do just about the same things with XML responses can you can with JSON responses. 194 | 195 | Here's a simple example: 196 | 197 | ``` 198 | > GOTO WITH-URL http://www-db.deis.unibo.it/courses/TW/DOCS/w3schools/xsl/books.xml 199 | STATUS 200 200 | http://www-db.deis.unibo.it/courses/TW/DOCS/w3schools/xsl/books.xml 201 | application/xml 202 | ``` 203 | 204 | Now let's do some XPath queries on the response 205 | 206 | ``` 207 | > SHOW XPATH /bookstore/book/title 208 | /bookstore/book/title 209 | 210 | Everyday Italian 211 | Harry Potter 212 | XQuery Kick Start 213 | Learning XML 214 | 215 | ``` 216 | 217 | And here's another one: 218 | 219 | ``` 220 | > SHOW PATH /bookstore/book/title 221 | /bookstore/book/title 222 | 223 | Everyday Italian 224 | Harry Potter 225 | XQuery Kick Start 226 | Learning XML 227 | 228 | ``` 229 | 230 | If you look carefully at the second example, you'll notice that the query command is `PATH`, not `XPATH`. That is because **HyperCLI** is smart enough to know that we're dealing with an XML document and will use the XPATH query engine instead of using the JSONPath query engine. 231 | 232 | To sum up, the `XPATH` and `JPATH` commands are explict calls to the XPath and JSONPath query engines in **HyperCLI**. But the `PATH` command uses the content type of the current document (`SHOW CONTENT-TYPE`) to help **HyperCLI** decide which query engine to use. 233 | 234 | ### Try Using SOAP Next Time 235 | Since **HyperCLI** is a fully-functional HTTP client, you can use it to make SOAP requests as well as simple HTTP requests. Currently **HyperLANG* does not have a plug-in for SOAP services (more on that in a future release) but you can still use straight-up HTTP requests in XML format to perform SOAP interactions. 236 | 237 | Here's a simple example. 238 | 239 | First, using **HyperCLI**, let's pull the WSDL document from a known SOAP service endpoint: 240 | 241 | ``` 242 | # pull WSDL 243 | > GOTO WITH-URL https://www.dataaccess.com/webservicesserver/NumberConversion.wso?WSDL 244 | STATUS 200 245 | https://www.dataaccess.com/webservicesserver/NumberConversion.wso?WSDL 246 | text/xml; charset=utf-8 247 | ``` 248 | 249 | Next, you can use the XPATH command to peek into the WSDL response document: 250 | 251 | ``` 252 | > SHOW XPATH //*[local-name(.)='operation']/@name 253 | name="NumberToWords" name="NumberToDollars" name="NumberToWords" name="NumberToDollars" name="NumberToWords" name="NumberToDollars" 254 | ``` 255 | 256 | Finally, with a bit of effort, you can craft a SOAP request to the `NumberToWords` operation and inspect the results: 257 | 258 | ``` 259 | GOTO WITH-URL https://www.dataaccess.com/webservicesserver/NumberConversion.wso WITH-BODY [% 500 %] WITH-METHOD post WITH-ENCODING text/xml 260 | STATUS 200 261 | https://www.dataaccess.com/webservicesserver/NumberConversion.wso 262 | text/xml; charset=utf-8 263 | > 264 | > # show full response 265 | > SHOW RESPONSE 266 | 267 | 268 | 269 | 270 | five hundred 271 | 272 | 273 | 274 | ``` 275 | 276 | You'll notice that **HyperLANG** now support `XPATH` queries, too! The support here is minimnal and will improve in the hear future. 277 | 278 | ### Hello, Hyper! 279 | 280 | Here's a super simple **HyperLANG** script: 281 | 282 | ``` 283 | GOTO https://company-atk.herokuapp.com 284 | ``` 285 | 286 | And here's how you do it: 287 | 288 | 1. Make sure you've installed the latest update of the **HyperCLI** : `$> npm install -g @mamund/hyper` 289 | 2. Launch the **HyperCLI** REPL : `$> hyper` 290 | 3. Type the command and press ENTER : `> GOTO https://company-atk.herokuapp.com` 291 | 292 | It may take a few seconds for the sample service to fire up but eventually, you should see the following response: 293 | 294 | ``` 295 | STATUS 200 296 | https://company-atk.herokuapp.com 297 | application/forms+json; charset=utf-8 298 | ``` 299 | 300 | You can also view the request/response details with these commands: 301 | 302 | ``` 303 | SHOW REQUEST 304 | SHOW RESPONSE 305 | SHOW METADATA 306 | SHOW ALL 307 | SHOW URL 308 | SHOW STATUS 309 | SHOW CONTENT-TYPE 310 | SHOW HEADERS 311 | ``` 312 | 313 | Finally, you can place all the **HyperLANG** commands in a text file and then pipe that file into the **HyperCLI** like this: 314 | 315 | ``` 316 | $> hyper