Alternatively, click New > Templates, search for "API Starter" then select the collection and
35 | click Run in Postman.
36 |
37 |
38 | Open the collection on the left, click the first request 0. Setup API and Send it.
39 | Follow the instructions the API sends back!
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Postman Galaxy - API Adoption Training
2 |
3 | This project is designed to support Postman training sessions. The API returns learning content for display inside Postman using the Postman visualizer. The API is complemented by a Postman template to guide learners through the steps to reach learning outcomes for the session. The API is developed and deployed on [Glitch](https://glitch.com/~galaxy-api-adoption) however you can deploy it somewhere else if you prefer.
4 |
5 | To try out the template, import it into Postman (instructions provided during the session). _Note that the template will not go live until the Postman Galaxy Conference in February 2021._
6 |
7 | ## Contribution and usage
8 |
9 | You can contribute to both the API and the template for the session–note that the experience is designed to teach the learning outcomes indicated by the Galaxy API Adoption badge criteria so please keep this in mind with any content changes. Trainee submissions also need to be testable by automation (learners submit their collection links and we run a script on the collection JSON to ensure that it contains required elements) so any changes to the completed template will need to be accommodated by the submission testing process.
10 |
11 | To contribute to the API:
12 |
13 | * Fork the [GitHub repo](https://github.com/SueSmith/galaxy-api-adoption).
14 | * In [Glitch](https://glitch.com/) create a new project, choosing **Import from GitHub**. Enter the URL for your fork of the repo.
15 | * Setup the following variables in the `env`:
16 | * `SECRET` - you'll need to pass this in the auth as `admin_key` from your requests if you need to access admin endpoints, just choose your own for your forked version of the API and include it in your forked template
17 | * `PROJECT` - this should just be the project name i.e. `Galaxy API Adoption` (needs to match the template name)
18 | * Make and test your changes on Glitch.
19 | * Export your Glitch project to your GitHub fork by choosing **Tools** > **Import and Export** > **Export to GitHub** (you will need to connect your GitHub and Glitch accounts–check out the [Glitch help](https://help-center.glitch.me/help/github/) if you need more info).
20 | * Once your changes are back on GitHub, open a pull request to merge them into the main repo (tip: include a link to your deployed Glitch app for testing).
21 | * _When your changes are merged, the repo owner will push them to the main Glitch app._
22 |
23 | While you're developing your API changes, you'll want to test them in Postman using the project template. You can also contribute to the template. If you're a member of the Postman company team:
24 |
25 | * Request access to the Postman team `Galaxy training` workspace (create an issue in the [repo](https://github.com/SueSmith/galaxy-api-adoption/issues) or ask [Sue](https://github.com/suesmith/)).
26 | * Fork the `Galaxy API Adoption` collection `training-session` branch–this is the collection as it will be on import, see the parent branch for an example of the collection state on completion by the learner.
27 | * Pop the URL for your forked version of the Glitch API into the collection request addresses (later in the session the URL is stored as a var but not initially).
28 | * Test any API changes you're making to the API using the template.
29 | * If you're contributing to the template, make your changes on your template fork and open a pull request inside Postman to have your changes merged into the main template. _Note that you will need to work through the existing API steps in order to try out your own components within the complete learning experience._
30 |
31 | ___If you're making changes to both the API and template and they need to be deployed in conjunction with one another, flag that up in both your pull requests (on GitHub and in Postman).___
32 |
33 | ## Feedback
34 |
35 | If you'd like to provide feedback or make a feature request / suggestion about the API or template, please feel free to [create an issue](https://github.com/SueSmith/galaxy-api-adoption/issues).
36 |
37 | ## Credits
38 |
39 | This API is based on the Glitch [hello-express](https://glitch.com/~hello-express) and [low-db](https://glitch.com/~low-db) apps.
40 |
--------------------------------------------------------------------------------
/shrinkwrap.yaml:
--------------------------------------------------------------------------------
1 | dependencies:
2 | body-parser: 1.19.0
3 | email-validator: 2.0.4
4 | express: 4.17.1
5 | faker: 5.1.0
6 | lowdb: 1.0.0
7 | shortid: 2.2.15
8 | packages:
9 | /accepts/1.3.7:
10 | dependencies:
11 | mime-types: 2.1.25
12 | negotiator: 0.6.2
13 | dev: false
14 | engines:
15 | node: '>= 0.6'
16 | resolution:
17 | integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
18 | /array-flatten/1.1.1:
19 | dev: false
20 | resolution:
21 | integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
22 | /body-parser/1.19.0:
23 | dependencies:
24 | bytes: 3.1.0
25 | content-type: 1.0.4
26 | debug: 2.6.9
27 | depd: 1.1.2
28 | http-errors: 1.7.2
29 | iconv-lite: 0.4.24
30 | on-finished: 2.3.0
31 | qs: 6.7.0
32 | raw-body: 2.4.0
33 | type-is: 1.6.18
34 | dev: false
35 | engines:
36 | node: '>= 0.8'
37 | resolution:
38 | integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
39 | /bytes/3.1.0:
40 | dev: false
41 | engines:
42 | node: '>= 0.8'
43 | resolution:
44 | integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
45 | /content-disposition/0.5.3:
46 | dependencies:
47 | safe-buffer: 5.1.2
48 | dev: false
49 | engines:
50 | node: '>= 0.6'
51 | resolution:
52 | integrity: sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
53 | /content-type/1.0.4:
54 | dev: false
55 | engines:
56 | node: '>= 0.6'
57 | resolution:
58 | integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
59 | /cookie-signature/1.0.6:
60 | dev: false
61 | resolution:
62 | integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
63 | /cookie/0.4.0:
64 | dev: false
65 | engines:
66 | node: '>= 0.6'
67 | resolution:
68 | integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
69 | /debug/2.6.9:
70 | dependencies:
71 | ms: 2.0.0
72 | dev: false
73 | resolution:
74 | integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
75 | /depd/1.1.2:
76 | dev: false
77 | engines:
78 | node: '>= 0.6'
79 | resolution:
80 | integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
81 | /destroy/1.0.4:
82 | dev: false
83 | resolution:
84 | integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
85 | /ee-first/1.1.1:
86 | dev: false
87 | resolution:
88 | integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
89 | /email-validator/2.0.4:
90 | dev: false
91 | engines:
92 | node: '>4.0'
93 | resolution:
94 | integrity: sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==
95 | /encodeurl/1.0.2:
96 | dev: false
97 | engines:
98 | node: '>= 0.8'
99 | resolution:
100 | integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
101 | /escape-html/1.0.3:
102 | dev: false
103 | resolution:
104 | integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
105 | /etag/1.8.1:
106 | dev: false
107 | engines:
108 | node: '>= 0.6'
109 | resolution:
110 | integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
111 | /express/4.17.1:
112 | dependencies:
113 | accepts: 1.3.7
114 | array-flatten: 1.1.1
115 | body-parser: 1.19.0
116 | content-disposition: 0.5.3
117 | content-type: 1.0.4
118 | cookie: 0.4.0
119 | cookie-signature: 1.0.6
120 | debug: 2.6.9
121 | depd: 1.1.2
122 | encodeurl: 1.0.2
123 | escape-html: 1.0.3
124 | etag: 1.8.1
125 | finalhandler: 1.1.2
126 | fresh: 0.5.2
127 | merge-descriptors: 1.0.1
128 | methods: 1.1.2
129 | on-finished: 2.3.0
130 | parseurl: 1.3.3
131 | path-to-regexp: 0.1.7
132 | proxy-addr: 2.0.5
133 | qs: 6.7.0
134 | range-parser: 1.2.1
135 | safe-buffer: 5.1.2
136 | send: 0.17.1
137 | serve-static: 1.14.1
138 | setprototypeof: 1.1.1
139 | statuses: 1.5.0
140 | type-is: 1.6.18
141 | utils-merge: 1.0.1
142 | vary: 1.1.2
143 | dev: false
144 | engines:
145 | node: '>= 0.10.0'
146 | resolution:
147 | integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
148 | /faker/5.1.0:
149 | dev: false
150 | resolution:
151 | integrity: sha512-RrWKFSSA/aNLP0g3o2WW1Zez7/MnMr7xkiZmoCfAGZmdkDQZ6l2KtuXHN5XjdvpRjDl8+3vf+Rrtl06Z352+Mw==
152 | /finalhandler/1.1.2:
153 | dependencies:
154 | debug: 2.6.9
155 | encodeurl: 1.0.2
156 | escape-html: 1.0.3
157 | on-finished: 2.3.0
158 | parseurl: 1.3.3
159 | statuses: 1.5.0
160 | unpipe: 1.0.0
161 | dev: false
162 | engines:
163 | node: '>= 0.8'
164 | resolution:
165 | integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
166 | /forwarded/0.1.2:
167 | dev: false
168 | engines:
169 | node: '>= 0.6'
170 | resolution:
171 | integrity: sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
172 | /fresh/0.5.2:
173 | dev: false
174 | engines:
175 | node: '>= 0.6'
176 | resolution:
177 | integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
178 | /graceful-fs/4.2.3:
179 | dev: false
180 | resolution:
181 | integrity: sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
182 | /http-errors/1.7.2:
183 | dependencies:
184 | depd: 1.1.2
185 | inherits: 2.0.3
186 | setprototypeof: 1.1.1
187 | statuses: 1.5.0
188 | toidentifier: 1.0.0
189 | dev: false
190 | engines:
191 | node: '>= 0.6'
192 | resolution:
193 | integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
194 | /http-errors/1.7.3:
195 | dependencies:
196 | depd: 1.1.2
197 | inherits: 2.0.4
198 | setprototypeof: 1.1.1
199 | statuses: 1.5.0
200 | toidentifier: 1.0.0
201 | dev: false
202 | engines:
203 | node: '>= 0.6'
204 | resolution:
205 | integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
206 | /iconv-lite/0.4.24:
207 | dependencies:
208 | safer-buffer: 2.1.2
209 | dev: false
210 | engines:
211 | node: '>=0.10.0'
212 | resolution:
213 | integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
214 | /inherits/2.0.3:
215 | dev: false
216 | resolution:
217 | integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
218 | /inherits/2.0.4:
219 | dev: false
220 | resolution:
221 | integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
222 | /ipaddr.js/1.9.0:
223 | dev: false
224 | engines:
225 | node: '>= 0.10'
226 | resolution:
227 | integrity: sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
228 | /is-promise/2.1.0:
229 | dev: false
230 | resolution:
231 | integrity: sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
232 | /lodash/4.17.15:
233 | dev: false
234 | resolution:
235 | integrity: sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
236 | /lowdb/1.0.0:
237 | dependencies:
238 | graceful-fs: 4.2.3
239 | is-promise: 2.1.0
240 | lodash: 4.17.15
241 | pify: 3.0.0
242 | steno: 0.4.4
243 | dev: false
244 | engines:
245 | node: '>=4'
246 | resolution:
247 | integrity: sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==
248 | /media-typer/0.3.0:
249 | dev: false
250 | engines:
251 | node: '>= 0.6'
252 | resolution:
253 | integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
254 | /merge-descriptors/1.0.1:
255 | dev: false
256 | resolution:
257 | integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
258 | /methods/1.1.2:
259 | dev: false
260 | engines:
261 | node: '>= 0.6'
262 | resolution:
263 | integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
264 | /mime-db/1.42.0:
265 | dev: false
266 | engines:
267 | node: '>= 0.6'
268 | resolution:
269 | integrity: sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==
270 | /mime-types/2.1.25:
271 | dependencies:
272 | mime-db: 1.42.0
273 | dev: false
274 | engines:
275 | node: '>= 0.6'
276 | resolution:
277 | integrity: sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==
278 | /mime/1.6.0:
279 | dev: false
280 | engines:
281 | node: '>=4'
282 | hasBin: true
283 | resolution:
284 | integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
285 | /ms/2.0.0:
286 | dev: false
287 | resolution:
288 | integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
289 | /ms/2.1.1:
290 | dev: false
291 | resolution:
292 | integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
293 | /nanoid/2.1.11:
294 | dev: false
295 | resolution:
296 | integrity: sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
297 | /negotiator/0.6.2:
298 | dev: false
299 | engines:
300 | node: '>= 0.6'
301 | resolution:
302 | integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
303 | /on-finished/2.3.0:
304 | dependencies:
305 | ee-first: 1.1.1
306 | dev: false
307 | engines:
308 | node: '>= 0.8'
309 | resolution:
310 | integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
311 | /parseurl/1.3.3:
312 | dev: false
313 | engines:
314 | node: '>= 0.8'
315 | resolution:
316 | integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
317 | /path-to-regexp/0.1.7:
318 | dev: false
319 | resolution:
320 | integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
321 | /pify/3.0.0:
322 | dev: false
323 | engines:
324 | node: '>=4'
325 | resolution:
326 | integrity: sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
327 | /proxy-addr/2.0.5:
328 | dependencies:
329 | forwarded: 0.1.2
330 | ipaddr.js: 1.9.0
331 | dev: false
332 | engines:
333 | node: '>= 0.10'
334 | resolution:
335 | integrity: sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
336 | /qs/6.7.0:
337 | dev: false
338 | engines:
339 | node: '>=0.6'
340 | resolution:
341 | integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
342 | /range-parser/1.2.1:
343 | dev: false
344 | engines:
345 | node: '>= 0.6'
346 | resolution:
347 | integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
348 | /raw-body/2.4.0:
349 | dependencies:
350 | bytes: 3.1.0
351 | http-errors: 1.7.2
352 | iconv-lite: 0.4.24
353 | unpipe: 1.0.0
354 | dev: false
355 | engines:
356 | node: '>= 0.8'
357 | resolution:
358 | integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
359 | /safe-buffer/5.1.2:
360 | dev: false
361 | resolution:
362 | integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
363 | /safer-buffer/2.1.2:
364 | dev: false
365 | resolution:
366 | integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
367 | /send/0.17.1:
368 | dependencies:
369 | debug: 2.6.9
370 | depd: 1.1.2
371 | destroy: 1.0.4
372 | encodeurl: 1.0.2
373 | escape-html: 1.0.3
374 | etag: 1.8.1
375 | fresh: 0.5.2
376 | http-errors: 1.7.3
377 | mime: 1.6.0
378 | ms: 2.1.1
379 | on-finished: 2.3.0
380 | range-parser: 1.2.1
381 | statuses: 1.5.0
382 | dev: false
383 | engines:
384 | node: '>= 0.8.0'
385 | resolution:
386 | integrity: sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
387 | /serve-static/1.14.1:
388 | dependencies:
389 | encodeurl: 1.0.2
390 | escape-html: 1.0.3
391 | parseurl: 1.3.3
392 | send: 0.17.1
393 | dev: false
394 | engines:
395 | node: '>= 0.8.0'
396 | resolution:
397 | integrity: sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
398 | /setprototypeof/1.1.1:
399 | dev: false
400 | resolution:
401 | integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
402 | /shortid/2.2.15:
403 | dependencies:
404 | nanoid: 2.1.11
405 | dev: false
406 | resolution:
407 | integrity: sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==
408 | /statuses/1.5.0:
409 | dev: false
410 | engines:
411 | node: '>= 0.6'
412 | resolution:
413 | integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
414 | /steno/0.4.4:
415 | dependencies:
416 | graceful-fs: 4.2.3
417 | dev: false
418 | resolution:
419 | integrity: sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=
420 | /toidentifier/1.0.0:
421 | dev: false
422 | engines:
423 | node: '>=0.6'
424 | resolution:
425 | integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
426 | /type-is/1.6.18:
427 | dependencies:
428 | media-typer: 0.3.0
429 | mime-types: 2.1.25
430 | dev: false
431 | engines:
432 | node: '>= 0.6'
433 | resolution:
434 | integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
435 | /unpipe/1.0.0:
436 | dev: false
437 | engines:
438 | node: '>= 0.8'
439 | resolution:
440 | integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
441 | /utils-merge/1.0.1:
442 | dev: false
443 | engines:
444 | node: '>= 0.4.0'
445 | resolution:
446 | integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
447 | /vary/1.1.2:
448 | dev: false
449 | engines:
450 | node: '>= 0.8'
451 | resolution:
452 | integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
453 | registry: 'https://registry.npmjs.org/'
454 | shrinkwrapMinorVersion: 9
455 | shrinkwrapVersion: 3
456 | specifiers:
457 | body-parser: ^1.19.0
458 | email-validator: ^2.0.4
459 | express: ^4.17.1
460 | faker: ^5.1.0
461 | lowdb: 1.x
462 | shortid: ^2.2.15
463 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /*
2 | POSTMAN GALAXY API ADOPTION
3 |
4 | This API works in conjunction with the Postman Galaxy API Adoption collection in Postman to walk you through API adoption skills.
5 | Import the collection into Postman from the workspace shared during the session and send a request to the setup endpoint to begin.
6 |
7 |
8 | This Glitch app is based on hello-express and low-db.
9 |
10 | Below you'll see the code for the endpoints in the API after some initial setup processing
11 | - each endpoint begins "app." followed by get, post, patch, put, or delete, then the endpoint path, e.g. /cat
12 | */
13 |
14 | /*
15 | response structure:
16 |
17 | {
18 | welcome:
19 | "Welcome! Check out the 'data' object below to see the values returned by the API. Click **Visualize** to see the 'tutorial' data " +
20 | "for this request in a more readable view.",
21 | data: {
22 | cat: {
23 | name: "Syd",
24 | humans: 9
25 | }
26 | },
27 | tutorial: {
28 | title: "You did a thing! 🚀",
29 | intro: "Here is the _intro_ to this **lesson**...",
30 | steps: [
31 | {
32 | note: "Here is a step with `code` in it...",
33 | pic:
34 | "https://assets.postman.com/postman-docs/postman-app-overview-response.jpg",
35 | raw_data: {
36 | cat: {
37 | name: "Syd",
38 | humans: 9
39 | }
40 | }
41 | }
42 | ],
43 | next: [
44 | {
45 | step: "Now do this...",
46 | pic:
47 | "https://assets.postman.com/postman-docs/postman-app-overview-response.jpg",
48 | raw_data: {
49 | cat: {
50 | name: "Syd",
51 | humans: 9
52 | }
53 | }
54 | }
55 | ]
56 | }
57 | }
58 | */
59 |
60 | // server.js
61 | // where your node app starts
62 |
63 | const express = require("express");
64 | var bodyParser = require("body-parser");
65 | const app = express();
66 | app.use(bodyParser.json());
67 | app.use(bodyParser.urlencoded({ extended: true }));
68 |
69 | // setup a new database persisted using async file storage
70 | // Security note: the database is saved to the file `db.json` on the local filesystem.
71 | // It's deliberately placed in the `.data` directory which doesn't get copied if someone remixes the project.
72 | var low = require("lowdb");
73 | var FileSync = require("lowdb/adapters/FileSync");
74 | var adapter = new FileSync(".data/db.json");
75 | var db = low(adapter);
76 | const shortid = require("shortid");
77 | //email validation
78 | var validator = require("email-validator");
79 | var faker = require("faker");
80 |
81 | // default list
82 | var defaultData = [];
83 | var i;
84 | for (i = 0; i < 5; i++)
85 | defaultData.push({
86 | id: shortid.generate(),
87 | phrase: faker.company.catchPhrase(),
88 | pic: faker.image.image(),
89 | num: Math.floor(Math.random() * 100) + 1
90 | });
91 | db.defaults({
92 | info: defaultData,
93 | calls: []
94 | }).write();
95 |
96 | // http://expressjs.com/en/starter/basic-routing.html
97 | app.get("/", (req, res) => {
98 | var newDate = new Date();
99 | db.get("calls")
100 | .push({
101 | when: newDate.toDateString() + " " + newDate.toTimeString(),
102 | where: "GET /",
103 | what: req.get("-")
104 | })
105 | .write();
106 | if (req.headers["user-agent"].includes("Postman"))
107 | res.status(200).json({
108 | welcome: welcomeMsg,
109 | tutorial: {
110 | title: "API Adoption",
111 | intro:
112 | "Use the API Adoption collection in Postman in conjunction with this API! " +
113 | "To see the API code navigate to https://glitch.com/edit/#!/galaxy-api-adoption in your web browser!"
114 | }
115 | });
116 | else
117 | res.send(
118 | "
API Adoption
Oh, hi! There's not much to see here - click the button on the right to view the code instead:
" +
119 | ''
120 | );
121 | });
122 |
123 | //generic welcome message
124 | var welcomeMsg =
125 | "You're using the API Adoption course! Check out the 'data' object below to see the values returned by this API request. " +
126 | "Click **Visualize** to see the 'tutorial' guiding you through next steps - do this for every request in the collection!";
127 | //admin unauthorized
128 | var unauthorizedMsg = {
129 | welcome: welcomeMsg,
130 | tutorial: {
131 | title: "Your request is unauthorized! 🚫",
132 | intro: "This endpoint requires admin authorization.",
133 | steps: [
134 | {
135 | note: "This endpoint is only accessible to admins for the API."
136 | }
137 | ],
138 | next: [
139 | {
140 | step: "Use the admin key indicated in the project env as secret."
141 | }
142 | ]
143 | }
144 | };
145 | //invalid route
146 | var invalidMsg = {
147 | welcome: welcomeMsg,
148 | tutorial: {
149 | title: "Your request is invalid! 🚧",
150 | intro:
151 | "Oops this isn't a valid endpoint! " +
152 | "Try undoing your changes or closing the request without saving and opening it again from the collection on the left of Postman."
153 | }
154 | };
155 |
156 | //intro
157 | app.get("/begin", (req, res) => {
158 | var newDate = new Date();
159 | db.get("calls")
160 | .push({
161 | when: newDate.toDateString() + " " + newDate.toTimeString(),
162 | where: "GET /begin",
163 | what: "-"
164 | })
165 | .write();
166 | res.status(200).json({
167 | welcome: welcomeMsg,
168 | data: {
169 | course: "API Adoption"
170 | },
171 | tutorial: {
172 | title: "Welcome to API Adoption training! 🛰️📣",
173 | intro:
174 | "This demo API will help you learn how to build adoption for an API using Postman. We'll " +
175 | "craft documentation, use mock data, visualize request responses, and promote the API by " +
176 | "publishing the collection and workspace. This will allow your users to onboard " +
177 | "faster, importing functional requests straight into Postman along with supporting resources.",
178 | steps: [
179 | {
180 | note:
181 | "This API provides a few demo endpoints that return random data generated using the faker library " +
182 | "(which we can also use in Postman as you will see later). _You are welcome to continue playing around with all of the requests " +
183 | "after the session–and feel free to remix the API on Glitch too: galaxy-api-adoption.glitch.me._ 😄"
184 | },
185 | {
186 | note:
187 | "The request you sent to the API received the following JSON response:",
188 | raw_data: {
189 | course: "API Adoption"
190 | }
191 | },
192 | {
193 | note:
194 | "> ✏️ Each request in the API is going to return some core data and the tutorial data that you can access in the Visualizer to " +
195 | "work through next steps. You will be switching between different views, so whenever you want to get back to the " +
196 | "instructions, select the request on the left or top and choose **Body** > **Visualize**. "
197 | }
198 | ],
199 | next: [
200 | {
201 | step:
202 | "Let's take a quick look at the collection documentation Postman generates by default. Click the little paper icon at the top " +
203 | "right of this request to open the docs. Click **View complete collection documentation** at the bottom and take a look before " +
204 | "returning here to the `Begin learning` request."
205 | },
206 | {
207 | step:
208 | "Postman will automatically populate your docs with request **methods**, **names**, **parameters**, **body data**, **auth info**"+
209 | ", and more. You can add lots of additional information to help users understand the purpose of your API endpoints and get "+
210 | "started using them. You can edit the details in the documentation view or within the individual requests / collection tabs. "+
211 | "_Use the little docs button on the right to show / hide the docs as we work through the requests._"
212 | },
213 | {
214 | step:
215 | "> ✏️ Notice that the address for this request starts with a base URL which is stored in a variable that you " +
216 | "imported as part of the collection. Hover over the var indicated by `{{url}}` in the address to see the value. Variables " +
217 | "let you reuse values throughout your collections and they will resolve in your documentation"
218 | },
219 | {
220 | step: "Open the next request `Get item` and hit **Send**."
221 | }
222 | ]
223 | }
224 | });
225 | });
226 |
227 | //get item, takes query param (returns demo data)
228 | app.get("/record", (req, res) => {
229 | var newDate = new Date();
230 | db.get("calls")
231 | .push({
232 | when: newDate.toDateString() + " " + newDate.toTimeString(),
233 | where: "GET /record",
234 | what: req.query.id
235 | })
236 | .write();
237 | if (req.query.id) {
238 | var records = db.get("info").value();
239 | var rec = records[Math.floor(Math.random() * records.length)];
240 | res.status(200).json({
241 | welcome: welcomeMsg,
242 | data: {
243 | record: rec
244 | },
245 | tutorial: {
246 | title: "You retrieved a record!",
247 | intro:
248 | "This endpoint retrieves a single record from the database. The request specified a record ID using the query parameter. " +
249 | "_The API doesn't really return the record with the id you specified, just a random record._ 🤫",
250 | steps: [
251 | {
252 | note: "The request returned the following data:",
253 | raw_data: {
254 | record: rec
255 | }
256 | },
257 | {
258 | note:
259 | "Aside from documentation, one of the best ways to help your users orient themselves with your API is to use meaningful " +
260 | "request names. Although this API returns junk data, let's pretend it doesn't. At the top of the request tab, hover over the " +
261 | "name `Get item` and click the little pencil icon to edit it. Give the request a more meaningful name of your choice, for " +
262 | "example `Get customer` (Postman will autosave)."
263 | },
264 | {
265 | note:
266 | "#### Add descriptions\n\nWith the documentation for the request open on the right, you'll see that you can add a description for the request. " +
267 | "Click the text-area with 'Make things easier...' etc in it and enter a request description. You can use markdown, for example:",
268 | js_code: [
269 | "Retrieve a single customer.",
270 | "",
271 | "_To specify the customer you want to retrieve:_",
272 | "* Add a **Query parameter** named `id`."
273 | ]
274 | },
275 | {
276 | note:
277 | "You can **Preview** to see the rendered markdown at any time. **Save** your description. Before you move on, also save the response, choosing **Save Response** " +
278 | "> **Save as example**–with the example also named `Get customer`, then return here and **Send** again."
279 | },
280 | {
281 | note:
282 | "#### Dynamic examples\n\nTo make the response a little more interesting, we can use dynamic faker variables to generate random values " +
283 | "whenever the example is generated. Back in the `Get customer` example, replace the value of the `data` > `phrase` property with " +
284 | '`{{$randomCatchPhrase}}` so that it looks like this: `\"phrase\": "{{$randomCatchPhrase}}",`'
285 | },
286 | {
287 | note:
288 | "When you start typing `{{$random`, you'll see that there are lots of different random variables which you can use to make " +
289 | "your docs and demos more dynamic. " +
290 | "**Save** the example as before. Check out the docs again, following the link to the complete version to see how the different examples " +
291 | "render–you can select them from the drop-down list."
292 | },
293 | {
294 | note:
295 | "#### Markdown elements\n\nYou can add links and images to your markdown. To try out an image, grab the URL for the `pic` property in " +
296 | "the response JSON and include it in your request docs markdown using the following syntax, saving to see the rendered image:",
297 | js_code: [""]
298 | },
299 | {
300 | note: "Try a link also:",
301 | js_code: ["[Your website](https://postman.com)"]
302 | }
303 | ],
304 | next: [
305 | {
306 | step:
307 | "You added request documentation, but you can also add information at collection and folder level. Providing a strong intro to your " +
308 | "collection will give your users the best chance of getting started, including universal info such as auth requirements, "+
309 | "and example uses cases / workflows."
310 | },
311 | {
312 | step: "The collection had a description when you imported it, so let's edit the folder intead. Click the **Learn adoption** folder "+
313 | "on the left and open its docs on the right. "+
314 | "Add something to the description there too like you did for the request, then come back to `Get customer`."
315 | },
316 | {
317 | step:
318 | "You can use collections to share information about your APIs in a variety of ways as we'll see in this session. To see a " +
319 | "preview of what your docs will look like if someone views them on the web outside Postman, **View complete collection " +
320 | "documentation**, click **Publish** > **Preview documentation** then navigate back here to `Get customer` using the browser back button."
321 | },
322 | {
323 | step: "Open the next request `POST` `Add item` and hit **Send**."
324 | }
325 | ]
326 | }
327 | });
328 | } else {
329 | res.status(404).json({
330 | welcome: welcomeMsg,
331 | data: {
332 | message: "Item not specified"
333 | },
334 | tutorial: {
335 | title: "Query parameter required!",
336 | intro:
337 | "This endpoint is going to return a record from the database. We're going to add information that will appear in the " +
338 | "documentation to help users of the API make a successful request.",
339 | steps: [
340 | {
341 | note:
342 | "The request in this case returned a `404 Not Found` error response because the endpoint " +
343 | "requires a query parameter named `id`."
344 | },
345 | {
346 | note:
347 | "Your docs can include example request and response data. The easiest way to create an example is to save a response–so let's " +
348 | "do that. Postman will autopopulate the example with the response you received, including the parameters sent, status code, "+
349 | "and body data. Just above the response area click **Save Response** and choose **Save as example**. You can edit "+
350 | "the example, which we'll do soon, but for now just **Save** it, return here, and send again."
351 | },
352 | {
353 | note:
354 | "In the docs view next to the request, click **View complete collection documentation** to see how the example " +
355 | "renders, then return here to `Get item`."
356 | },
357 | {
358 | note:
359 | "> ✏️ The complete docs will open in a separate tab inside Postman " +
360 | "so you can toggle back and forth between them and the request you're working on."
361 | }
362 | ],
363 | next: [
364 | {
365 | step:
366 | "In **Params**, check the (initially unchecked) query parameter named `id` so that it will be sent the next time we send the request. "+
367 | "Add some **Description** text for the `id`, for example `Record id to retrieve`. Click **Save** and " +
368 | "check out the docs again–the parameter and description appear inline."
369 | },
370 | {
371 | step:
372 | "Being able to share your collection (and workspace) lets you provide users with prebuilt requests " +
373 | "they can send alongside information you author (also inside Postman). With the query parameter added and your request " +
374 | "saved, click **Send**."
375 | }
376 | ]
377 | }
378 | });
379 | }
380 | });
381 |
382 | //get items
383 | app.get("/records", (req, res) => {
384 | var newDate = new Date();
385 | db.get("calls")
386 | .push({
387 | when: newDate.toDateString() + " " + newDate.toTimeString(),
388 | where: "GET /records",
389 | what: "-"
390 | })
391 | .write();
392 | var data = db.get("info").value();
393 | res.status(200).json({
394 | welcome: welcomeMsg,
395 | data: {
396 | records: data
397 | },
398 | tutorial: {
399 | title: "You retrieved all records!",
400 | intro:
401 | "We've used examples to populate the collection documentation, but we can also use them to return mock data instead of " +
402 | "connecting to our production API.",
403 | steps: [
404 | {
405 | note:
406 | "Save the response to this request as an example like you did before and return here. We're going to create a mock server for " +
407 | "this collection, and you will be able to switch between the mock API and the 'production' one we've been using so far, using an " +
408 | "environment. The URL for the API is stored in a collection variable, but we're going to store the mock one in an " +
409 | "environment variable."
410 | },
411 | {
412 | note:
413 | "> ✏️ Postman variable scope means that if the environment and collection have a variable with the same name, the value in " +
414 | "the environment will override the collection value, so we're going to add a `url` var to the environment and it will determine " +
415 | "where the requests send–as long as the environment is selected. If we deselect the environment, the request addresses will " +
416 | "again use the collection variable URL value."
417 | }
418 | ],
419 | next: [
420 | {
421 | step:
422 | "In **Mock Servers** on the left, create a new mock server and select the existing `Galaxy API Adoption` collection. Enter a " +
423 | "name for the mock, check **Save the mock server URL as an environment variable** and create the mock server before returning "+
424 | "here to `Get list`."
425 | },
426 | {
427 | step:
428 | "Select the new environment from the list at the top right of Postman so that it becomes the _active_ environment (it will have "+
429 | "the same name as the mock). Click " +
430 | "the eye button to see that the value of the variable has been set to the new mock server. In the `Get list` request hover over " +
431 | "the `url` in the address to see that it now points at the mock."
432 | },
433 | {
434 | step: "**Send** to make sure the request returns the same " +
435 | "response. Try out another request you added an example to, to check that it still works from the mock URL."
436 | },
437 | {
438 | step:
439 | "> ✏️ If you're in doubt about where a request sent, check out the **Console** at the bottom left of Postman. " +
440 | "Note that the only requests with saved examples will return a response from the mock server. 😉"
441 | },
442 | {
443 | step: "Before you move on–**deselect the environment using the control at the top right** so that it reads `No environment`."
444 | },
445 | {
446 | step:
447 | "#### Visualizing data\n\nBefore we finish with this collection, let's take a look at what you can do with data visualization in Postman. The " +
448 | "instructions you've been reading use a script in the collection that renders a tutorial in the **Visualize** view, based on the "+
449 | "response data returned by the API. You can do much more than this to make your response data come to life, including charts and "+
450 | "graphs. 🖼️📊"
451 | },
452 | {
453 | step:
454 | "Go back into `Get customer` and open **Tests**. You'll see some code in there with part of it commented out. Remove the " +
455 | "`/*` and `*/` at the start and end of the script (so that the code is no longer commented out). Hit **Send** to see what the "+
456 | "visualizer script does with the response data. Take a look at the HTML inside the `template` in the script to see how this pulls "+
457 | "data from the response and displays it. Come back to the `Get list` request."
458 | },
459 | {
460 | step:
461 | "> ✏️ Note that the catchphrase text will change each time you make the request to the mock server because it uses " +
462 | "a dynamic variable, as it will whenever the documentation loads. Try experimenting with other dynamic variables in your " +
463 | "examples to introduce more randomness into your demos."
464 | },
465 | {
466 | step:
467 | "Do the same here in `Get list`, uncommenting out the visualizer code in **Tests**. This one is more complex, using chart.js to " +
468 | "render a pie chart with the values from the response array. Try tweaking the visualizer code to change the display!"
469 | },
470 | {
471 | step:
472 | "When you're happy you've completed all the steps and are ready to check your collection for completeness so that you can "+
473 | "claim your API Adoption badge–open the final request `Test collection`, in the `Complete submission` folder and check out the docs "+
474 | "for instructions."
475 | }
476 | ]
477 | }
478 | });
479 | });
480 |
481 | //add record - does not really add to db
482 | app.post("/record", (req, res) => {
483 | const apiSecret = req.get("api_key");
484 | var newDate = new Date();
485 | db.get("calls")
486 | .push({
487 | when: newDate.toDateString() + " " + newDate.toTimeString(),
488 | where: "POST /record",
489 | what: apiSecret
490 | })
491 | .write();
492 | if (!apiSecret || apiSecret.length < 1 || apiSecret.startsWith("{")) {
493 | res.status(401).json({
494 | welcome: welcomeMsg,
495 | tutorial: {
496 | title: "Oops - You got an unauthorized error response! 🚫",
497 | intro:
498 | "Your request needs to include an API key. This is a `POST` request to add new data, so needs an API key in order to verify " +
499 | "permissions to update the data source. 🔒",
500 | data: {
501 | message: "No API key included"
502 | },
503 | steps: [
504 | {
505 | note:
506 | "When your API requires auth and you're publishing from a Postman collection that you want people to be able to send requests from, " +
507 | "you need to be careful not to expose credentials to documentation viewers–_you also want to set users up to make authenticated " +
508 | "requests with the minimum of hassle, as this can be a huge stumbling block for people trying to onboard with your API_. " +
509 | "Let's use a variable for the API key, so that you can highlight how users should authenticate, while still avoiding leaking " +
510 | "your own credentials."
511 | },
512 | {
513 | note:
514 | "Create an environment–choose **Environments** on the left, then the **+** button. Give it the name `Adoption env` and add a single " +
515 | "variable named `auth_key`, with any text string value you like e.g. `abc123` then **Save**. Select the environment either at the top right in " +
516 | "the drop-down list, or on the left by hovering over it and clicking the ✔️ check mark to make it _active_."
517 | },
518 | {
519 | note:
520 | "Select the `Galaxy API Adoption` collection and in **Authorization** choose **API Key**, adding a key named `api_key` with " +
521 | "the value `{{auth_key}}` to use your variable, and the **Header** selected, then come back to the `POST` request."
522 | }
523 | ],
524 | next: [
525 | {
526 | step:
527 | "In the `POST` `Add item` request, open **Authorization** and select **Inherit auth from parent**–this will use the collection level " +
528 | "auth settings. Open the complete collection documentation and scroll to the top to see how the auth info is represented. 🔑"
529 | },
530 | {
531 | step:
532 | "> ✏️ You can publish environments along with collections when you make them public, for example if you provide a " +
533 | "sandbox environment your API users can interact with–make sure you **do not publish an environment** if " +
534 | "it contains credentials you don't want to expose."
535 | },
536 | {
537 | step:
538 | "Make sure you have selected the environment you created to make it active. Click **Send**."
539 | }
540 | ]
541 | }
542 | });
543 | } else if (!req.body.id)
544 | res.status(400).json({
545 | welcome: welcomeMsg,
546 | tutorial: {
547 | title: "Your request is incomplete! ✋",
548 | intro:
549 | "You need to provide the data you want to update the record with.",
550 | data: {
551 | message: "No body data included"
552 | },
553 | steps: [
554 | {
555 | note:
556 | "In **Body** select **raw** and choose **JSON** instead of `Text` in the drop-down list. Enter the following " +
557 | "including the enclosing curly braces:",
558 | raw_data: {
559 | id: "{{$randomLoremWord}}",
560 | phrase: "{{$randomCatchPhrase}}",
561 | pic: "http://placeimg.com/640/480/cats",
562 | num: 10
563 | }
564 | }
565 | ],
566 | next: [
567 | {
568 | step: "With your body data in place, click **Send** again."
569 | }
570 | ]
571 | }
572 | });
573 | else {
574 | res.status(201).json({
575 | welcome: welcomeMsg,
576 | tutorial: {
577 | title: "You added a new record! ",
578 | intro:
579 | "_You didn't really add a new record, this is just a demo endpoint.._ 🙈",
580 | data: {
581 | message: "Record added"
582 | },
583 | steps: [
584 | {
585 | note:
586 | "You've seen how collection level info such as auth renders in the documentation–remember that this collection also uses " +
587 | "folders. Postman will automatically structure your docs using folders and the list of requests–clicking these lets users " +
588 | "navigate through the information."
589 | },
590 | {
591 | note:
592 | "When your users view the collection docs on the web inside a Postman workspace, they can click **Open Request** to go straight " +
593 | "to a request from the description. You can send people links to all of these components inside a workspace and know that the " +
594 | "viewer will arrive at whatever resource you want to share."
595 | },
596 | {
597 | note:
598 | "Let's make the **Body** data a little more dynamic using faker variables. Replace the value of the `id` property with `{{$randomLoremWord}}`"+
599 | " and the value of `phrase` with `{{$randomCatchPhrase}}` so that it looks something like this:",
600 | raw_data: {
601 | id: "{{$randomLoremWord}}",
602 | phrase: "{{$randomCatchPhrase}}",
603 | pic: "http://placeimg.com/640/480/cats",
604 | num: 10
605 | }
606 | },
607 | {
608 | note:
609 | "Postman will generate these each time the docs are viewed or the request is sent."
610 | }
611 | ],
612 | next: [
613 | {
614 | step:
615 | "Before you move on, save the response to this request as an example like you did before by choosing **...** > **Add " +
616 | "example** > **Save**, and come back here to the `POST` request."
617 | },
618 | {
619 | step: "Next open the `PUT` `Update item` request and hit **Send**."
620 | }
621 | ]
622 | }
623 | });
624 | }
625 | });
626 |
627 | //update item - does not really update
628 | app.put("/record", function(req, res) {
629 | const apiSecret = req.get("api_key");
630 | var newDate = new Date();
631 | db.get("calls")
632 | .push({
633 | when: newDate.toDateString() + " " + newDate.toTimeString(),
634 | where: "PUT /record",
635 | what: apiSecret
636 | })
637 | .write();
638 | if (!apiSecret)
639 | res.status(401).json({
640 | welcome: welcomeMsg,
641 | tutorial: {
642 | title: "Oops - You got an unauthorized error response! 🚫",
643 | intro: "Your request needs to include an API key.",
644 | data: {
645 | message: "No API key included"
646 | },
647 | steps: [
648 | {
649 | note: "In **Authorization** select **Inherit auth from parent**."
650 | }
651 | ],
652 | next: [
653 | {
654 | step: "With your API key added to the request, click **Send**."
655 | }
656 | ]
657 | }
658 | });
659 | else if (!req.query.id)
660 | res.status(400).json({
661 | welcome: welcomeMsg,
662 | tutorial: {
663 | title: "Your request is missing some info! 📭",
664 | intro:
665 | "In order to update a record you need to provide the ID for the record you want to update.",
666 | data: {
667 | message: "No id included"
668 | },
669 | steps: [
670 | {
671 | note:
672 | "In **Params** add `id` in the **Key** column, and any text value you like as the **Value**."
673 | }
674 | ],
675 | next: [
676 | {
677 | step: "With your parameter in place, click **Send** again."
678 | }
679 | ]
680 | }
681 | });
682 | else if (!req.body.num)
683 | res.status(400).json({
684 | welcome: welcomeMsg,
685 | tutorial: {
686 | title: "Your request is incomplete! ✋",
687 | intro:
688 | "You need to provide the data you want to update the record with.",
689 | data: {
690 | message: "No body data included"
691 | },
692 | steps: [
693 | {
694 | note:
695 | "In **Body** select **raw** and choose **JSON** instead of `Text` in the drop-down list. Enter the following " +
696 | "including the enclosing curly braces:",
697 | raw_data: {
698 | num: 10
699 | }
700 | }
701 | ],
702 | next: [
703 | {
704 | step: "With your body data in place, click **Send** again."
705 | }
706 | ]
707 | }
708 | });
709 | else {
710 | res.status(201).json({
711 | welcome: welcomeMsg,
712 | tutorial: {
713 | title: "You updated a record! ✅",
714 | intro:
715 | "_You didn't really update a record, this is just a demo endpoint.._ 🙈",
716 | data: {
717 | message: "Record updated"
718 | },
719 | steps: [
720 | {
721 | note:
722 | "You can share collections with your users in multiple ways–by publishing documentation / including a **Run in Postman** " +
723 | "button on your site, publishing a template, or sharing from a public workspace. Your users can "+
724 | "import collections from the button in your docs / site, or by forking from a workspace like you did at "+
725 | "the start of the session."
726 | },
727 | {
728 | note:
729 | "When you create a public workspace, users can see collections, requests, environments, and more " +
730 | "_in context inside Postman–and you can create links to all of these_. In order to use a " +
731 | "collection from a workspace, your audience will fork it into their own workspace where they can edit and send requests."
732 | },
733 | {
734 | note:
735 | "The number of collection forks is an indicator of engagement with the resources in a public workspace. Users can also " +
736 | "comment on your public workspace, and you can even accept pull requests to create an open source contribution pathway. 🚀"
737 | }
738 | ],
739 | next: [
740 | {
741 | step:
742 | "> ✏️ If you just need to share a collection with someone, you can use the collection's public link–from " +
743 | "the collection **Share** option, choosing **Get public link**, then generate or update the link and copy it. _Note that you will " +
744 | "need to update the link each time you make a change to the collection as the link acts as a snapshot of the collection._ " +
745 | "We are going to use this method to check your training session progress before you submit your collection to receive the " +
746 | "API Adoption badge.. 👀"
747 | },
748 | {
749 | step:
750 | "Save the response to this request as an example like you did before, then open the `DEL` `Remove item` request and hit **Send**."
751 | }
752 | ]
753 | }
754 | });
755 | }
756 | });
757 |
758 | //delete item - doesn't really
759 | app.delete("/record/:id", function(req, res) {
760 | const apiSecret = req.get("api_key");
761 | var newDate = new Date();
762 | db.get("calls")
763 | .push({
764 | when: newDate.toDateString() + " " + newDate.toTimeString(),
765 | where: "DEL /record",
766 | what: apiSecret
767 | })
768 | .write();
769 | if (!apiSecret)
770 | res.status(401).json({
771 | welcome: welcomeMsg,
772 | tutorial: {
773 | title: "Oops - You got an unauthorized error response! 🚫",
774 | intro: "Your request needs to include an API key.",
775 | data: {
776 | message: "No API key included"
777 | },
778 | steps: [
779 | {
780 | note: "In **Authorization** select **Inherit auth from parent**"
781 | }
782 | ],
783 | next: [
784 | {
785 | step: "With your API key added to the request, click **Send**."
786 | }
787 | ]
788 | }
789 | });
790 | else {
791 | res.status(200).json({
792 | welcome: welcomeMsg,
793 | tutorial: {
794 | title: "You deleted a record! 🗑️",
795 | intro:
796 | "_You didn't really delete a record, this is just a demo endpoint.._ 😆",
797 | data: {
798 | message: "Record removed"
799 | },
800 | steps: [
801 | {
802 | note:
803 | "So far we have looked at how you can provide information to help your users onboard with your APIs, but you can also " +
804 | "share collections and documentation privately with collaborators using team workspaces. Members can see your collections "+
805 | "together with documentation, and can view docs from a web link."
806 | },
807 | {
808 | note:
809 | "Next up we are going to use mock servers to return fake data instead of letting users hit a " +
810 | "production API, and see some data visualizations on request responses."
811 | }
812 | ],
813 | next: [
814 | {
815 | step:
816 | "Save the response to this request as an example like you did before, then open the `Get list` request and hit **Send**."
817 | }
818 | ]
819 | }
820 | });
821 | }
822 | });
823 |
824 | app.get("/publish", (req, res) => {
825 | res.status(200).json({
826 | welcome: welcomeMsg,
827 | tutorial: {
828 | title: "Publish your workspace! 📣",
829 | intro:
830 | "Finally, we are going to walk through publishing your workspace and updating your Postman profile. ✨",
831 | data: {
832 | message: "Publish workspace and profile"
833 | },
834 | steps: [
835 | {
836 | note:
837 | "In order to publish your completed collection, you'll need to make your Postman profile public (if it isn't already). "+
838 | "Click your avatar at the top right of Postman and choose **View Profile**. Click **Edit Profile** and switch **Make Profile "+
839 | "Public** on if it is currently off. Add details including any images and links you want to be visible to your audience. "+
840 | "**Save** and return to your workspace."
841 | }
842 | ],
843 | next: [
844 | {
845 | step:
846 | "Now that your profile is public, you can go ahead and publish the workspace containing your training template (if you didn't "+
847 | "start with a public one)! Click the name of "+
848 | "the workspace at the top left of Postman to open the workspace overview. You can add a summary and description. "+
849 | "When you're ready, use the **Sharing** > **Visibility** control on the right to switch the workspace to **Public**!"
850 | },
851 | {
852 | step:
853 | "> ✏️ Your account admin can set your Postman workspaces to require approval before publishing–with the community manager role."
854 | },
855 | {
856 | step: "Open the final request in the collection `Test collection` and check out the docs for instructions on completing your submission."
857 | },
858 | {
859 | step: "> ✏️ Pop the URL for your collection in as the address (select your "+
860 | "collection, choose **Share** > **Get public link** and generate or update it, then copy and paste into the `Test collection`"+
861 | " address). Check your public workspace URL in another browser or private tab–you can copy it from the browser address"+
862 | " bar or navigate via the **Workspaces** drop-down at any time. Add the URL for your public workspace as the `workspace` value "+
863 | "in the `Test collection` **Headers** and hit "+
864 | "**Send** to check your collection for completeness, before filling out the form to get your badge and swag!"
865 | }
866 | ]
867 | }
868 | });
869 | });
870 |
871 | //ADMIN ENDPOINTS
872 |
873 | // removes entries from users and populates it with default users
874 | app.get("/reset", (req, res) => {
875 | const apiSecret = req.get("admin_key");
876 | if (!apiSecret || apiSecret !== process.env.SECRET) {
877 | res.status(401).json(unauthorizedMsg);
878 | } else {
879 | // removes all entries from the collection
880 | db.get("info")
881 | .remove()
882 | .write();
883 | console.log("Database cleared");
884 | var defaultData = [];
885 | var i;
886 | for (i = 0; i < 5; i++)
887 | db.get("info")
888 | .push({
889 | id: shortid.generate(),
890 | phrase: faker.company.catchPhrase(),
891 | pic: faker.image.image(),
892 | num: Math.floor(Math.random() * 100) + 1
893 | })
894 | .write();
895 |
896 | console.log("Default data added");
897 | res.status(200).json({
898 | welcome: welcomeMsg,
899 | tutorial: {
900 | title: "Database reset",
901 | intro: "You reset the DB."
902 | }
903 | });
904 | }
905 | });
906 |
907 | // removes all entries from the collection
908 | app.get("/clear", (req, res) => {
909 | const apiSecret = req.get("admin_key");
910 | if (!apiSecret || apiSecret !== process.env.SECRET) {
911 | res.status(401).json(unauthorizedMsg);
912 | } else {
913 | // removes all entries from the collection
914 | db.get("info")
915 | .remove()
916 | .write();
917 | console.log("Database cleared");
918 | res.status(200).json({
919 | welcome: welcomeMsg,
920 | tutorial: {
921 | title: "Database cleared",
922 | intro: "You cleared the DB."
923 | }
924 | });
925 | }
926 | });
927 |
928 | //get calls
929 | app.get("/calls", (req, res) => {
930 | const apiSecret = req.get("admin_key");
931 | if (!apiSecret || apiSecret !== process.env.SECRET) {
932 | res.status(401).json(unauthorizedMsg);
933 | } else {
934 | // removes all entries from the collection
935 | var allCalls = db.get("calls").value();
936 | res.status(200).json({
937 | welcome: welcomeMsg,
938 | data: allCalls,
939 | tutorial: {
940 | title: "All calls",
941 | intro: "The calls are as follows:",
942 | steps: [
943 | {
944 | raw_data: allCalls
945 | }
946 | ]
947 | }
948 | });
949 | }
950 | });
951 |
952 | //reset calls
953 | app.delete("/calls", function(req, res) {
954 | const apiSecret = req.get("admin_key");
955 | if (!apiSecret || apiSecret !== process.env.SECRET) {
956 | res.status(401).json(unauthorizedMsg);
957 | } else {
958 | // removes all entries from the collection
959 | db.get("calls")
960 | .remove()
961 | .write();
962 | res.status(200).json({
963 | welcome: welcomeMsg,
964 | tutorial: {
965 | title: "Calls deleted",
966 | intro: "You deleted the calls."
967 | }
968 | });
969 | }
970 | });
971 |
972 | //generic get error
973 | app.get("/*", (req, res) => {
974 | res.status(400).json(invalidMsg);
975 | });
976 | app.post("/*", (req, res) => {
977 | res.status(400).json(invalidMsg);
978 | });
979 | app.put("/*", (req, res) => {
980 | res.status(400).json(invalidMsg);
981 | });
982 | app.patch("/*", (req, res) => {
983 | res.status(400).json(invalidMsg);
984 | });
985 | app.delete("/*", (req, res) => {
986 | res.status(400).json(invalidMsg);
987 | });
988 |
989 | // listen for requests :)
990 | const listener = app.listen(process.env.PORT, () => {
991 | console.log("Your app is listening on port " + listener.address().port);
992 | });
993 |
--------------------------------------------------------------------------------