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