├── .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