import { addBanner, addArticle, addTitle, addHeader, addParagraph, addClassParagraph, addSubHeader, addOrderedList, addUnorderedList, addBlockquote, addInset, addInsetList, addInsetCodeListing, addInsetBulletList, addImageWithCaption, addButtonGroup, addSidebar, addSyntax, menu, global_menu } from '/scripts/import.js'; import { local_menu } from '/scripts/webdev.js'; const heading = document.querySelector(".heading"); const global = document.querySelector(".global_menu"); const local = document.querySelector(".local_menu"); const sidebar = document.querySelector(".sidebar"); const main = document.querySelector(".main_content"); heading.append(addTitle("Introducing Postman")); heading.append(addParagraph("David Fancher - LinkedIn Learning - April 2019")); heading.append(addParagraph("Chapter 4 - Environments and Variables")); main.append(addHeader("What is an Environment?")) main.append(addParagraph("Most of the requests we have sent so far used a URL based on the localhost such as")) main.append(addSyntax("http://localhost:3000/api/boards")) main.append(addParagraph("We could break this down into two parts, the server")) main.append(addSyntax("http://localhost:3000")) main.append(addParagraph("and the API route.")) main.append(addSyntax("/api/boards")) main.append(addParagraph("We might want to send this request to a different server. For example, we might use this URL for testing purposes and then later send the same request to a production server, so the first part of the request can change, but second part would remain the same.")) main.append(addParagraph("In Postman, an envornment is a set of variables we can set and later reference in a request to make it easier to switch between different contexts. Postman allows you to create multiple variables and you can easily switch between them so they can be a very useful tool.")) main.append(addParagraph("In the video for this part, David mentions an option in Settings - Automatically persist variable values which which will \"perist the current value of variables to the initial value at the end of every request execution\" which is probably not the clearest description!")) main.append(addParagraph("What this means is that if the variable is changed when a request is executed, Postman will update the variable to reflect that change. I don't have enough experience with Postman to know what the advantages and disadvantages of this are and this option seems to be no longer present in the version I am running. David does recomment that this is disabled before we start to work with environments. I am metioning that here in case the option is encountered at a later date either because of a future update to the software or if I am using an older version that still has it.")) main.append(addHeader("Managing Environments")) main.append(addParagraph("If you look in the top right corner of Postman, you will see a box labelled 'No Environment' although this may change when you start to use environments. In older versions of Postman, there are two icons next to it and if you hover over these, you will see that these are the Manage Environments and Environment quick look buttons. In the current version, we don't have the Manage Environments button which was used to add an environment. Rather, if you click on the Quick look tab , you will see a list of environments (if there are any, at this stage there are none for me) and an option to add a new environment so essentially that Manage Environments button has been merged with the list of environments.")) main.append(addParagraph("Note that in this section, we will create an environment called Local, but we will not use it until we get to the next section so we are going to start by clicking on the Environment quick look button and then Add in the Envronment box (see figure 52).")) main.append(addImageWithCaption("images/environment_quick_look.png", "Figure 52 - The box that appears when you click on Environment quick look.")) main.append(addParagraph("Clicking Add brings up the environment editor. By default, the name appears as New Envornment so we will start by setting that to Local. The editor itself looks reasonably familiar by now but there are some differences and some different options so I will go through each of these in turn.")) main.append(addSubHeader("VARIABLE")) main.append(addParagraph("This is perfectly straightforward, this is just the name we want to give our variable which we will later use to reference it.")) main.append(addSubHeader("TYPE")) main.append(addParagraph("The type can be default which means that values are displayed as plain text or secret which enables masking of the variables.")) main.append(addSubHeader("INITIAL VALUE")) main.append(addParagraph("Again, fairly straightfforward in that this is the initial value we give the variable.")) main.append(addSubHeader("CURRENT VALUE")) main.append(addParagraph("Any changes to the value of the variable will be reflected in the current value but will not change the initial value unless we choose persist the current value.")) main.append(addSubHeader("Persist All")) main.append(addParagraph("This will copy all of the current values over to the initial values.")) main.append(addSubHeader("Reset All")) main.append(addParagraph("This has the opposite effect to Persist All in that it copies all of the initial values over to the current values.")) main.append(addParagraph("There are a couple of things worth noting here. As was noted with other editors, we can disable or enable each of the elements (in this case, variables) by checking or unchecking the box to the left. This can also be useful if you want to use either the persist all or reset all option but have it ignore one of the variables, you can disable it since both of these options only affect enalbe variables, perform the operation and then re-enable the variable.")) main.append(addParagraph("The other thing I wanted to mention is perhaps best explained with an example as shown in figure 53.")) main.append(addImageWithCaption("images/local_variables.png", "Figure 53 - A couple of sample variables.")) main.append(addParagraph("Here we have a couple of variables with nonsensical initial values and I have added more realistic current values. Now, if I click Persist All, the current values will be copied over to the initial values as shown in figure 54.")) main.append(addImageWithCaption("images/local_variables1.png", "Figure 54 - The same variables after clicking Persist All.")) main.append(addParagraph("The question here is what will happen if we click Reset All, will the variables be reset to those initial nonsensical values or will they stay the same and the answer is, they stay the same. The initial value therefore refers to whatever the initial value currently holds rather than the value it was initially given so in this case, since the initial values are already the same as the current values, clicking Reset All has no effect - and indeed, clicking Persist All will also, as you might imagine, have no effect for the same reason.")) main.append(addParagraph("This begs the question, why do we have both initial and current values and there are a couple of reasons why you might want to do that. Firstly, this allows you to set a variable to a known good or perhaps just base value which you can then experiment with by changing the current value, knowing that if something breaks, you can always revert to the inital value. You could also do this by using multiple variables for experimentation purposes, eg, BASE_URL, BASE_URL_1 and so on.")) main.append(addParagraph("Secondly, and more importantly, we can share the environment and if we do, only the initial values are shared so this does allow us to isolate sensitive data. For example, we might have a variable for password where we insert a dummy password into the initial value and an actual password in the current value.")) main.append(addParagraph("To the top right in the Environment editor, there are Save and Share buttons and some breadcrumbs which opens a menu providing additional options for managing an environment and from there, we can Export, Delete or create a fork of the current Environment amongst other things.")) main.append(addParagraph("We can use the Share button if we want to share an environment with our team, but for now, I will just save it.")) main.append(addParagraph("If you click on the Environment quick look this will again bring up the box as shown in figure 52. From here, we can add global variables by clicking on Add in the Global box and this will open the global variables editor. This is similar to the Environment editor. The main difference is that global variables are going to be accessible from anywhere in Postman, regardless of which environment (if any) is currently active. Using global variables in this way is similar to using a global variable in coding and it has pretty much the same advantages and disadvantages.")) main.append(addParagraph("If you use both environment and global variables, this raises the possibility of a naming collision. If you use the same variable name in both the current environment and as a global variable, the variable in the current environment takes precedence so this might give you unexpected results if there is a collision you are unaware of where you expect the value of the global varualbe to be applied. Personally, I wouldn't imagine that I would use global variables but if you do need to, I would suggest adopting a naming convention to avoid such problems. For example, you might attach the letter G to the start or end of the variable name to indicate that it is a global variable. It may also be worth noting that the variable names we have seen in figures 53 and 54 use upper case only and this is because it makes it a lot easier to see these variables when they are being used so it is a very practical convention.")) main.append(addHeader("Using Variables in Requests")) main.append(addParagraph("I am going to send the boards request again and show the results in figure 55.")) main.append(addImageWithCaption("images/authenticated_request.png", "Figure 55 - The same variables after clicking Persist All.")) main.append(addParagraph("You might notice that this is the same image shown in figure 33 and I wanted to show it again to point out that each board has an unwieldy looking id. If you want to search in a particular board, you can copy that id and paste it into a request. However, it would probably be a lot easier and tidier to set up a variable to hold the id. In this case, I will copy the first id so that I can do exactly that.")) main.append(addParagraph("In order to be able to manage variables, we can open the environment editor so I will select that environment, click on the Environment quick look icon and select Edit for that environment as shown in figure 56.")) main.append(addImageWithCaption("images/edit_local.png", "Figure 56 - Edit the Local environment.")) main.append(addParagraph("I will create a variable called BOARD_ID and paste the id into the current value field. There are two reasons for that, the first being that we can share the environment without necessarily sharing that board id but also it helps to indicate that this is a variable that is likely to be changed quite often so it doesn't really make sense to specify an initial value.")) main.append(addParagraph("I will also add a second variable called BEARER_TOKEN and this will allow us to easily add a token into any requests we run. For the time being, I will not give it any value.")) main.append(addParagraph("We can also edit the values of existing variables by clicking on Environment quick look as shown in figure 57.")) main.append(addImageWithCaption("images/environment_quick_look1.png", "Figure 57 - Examining the variables in Environment quick look.")) main.append(addParagraph("The mouse is hovering over the BOARD_ID variable and as you can see, there is an edit icon which we can click. This opens up the field holding the value and we can edit it here or paste in a new value.")) main.append(addHeader("Using Variables in Requests")) main.append(addParagraph("Now that we have some variable names created, we can reference these by enclosing them in double curly braces and we can do that wherever we would previously have used the literal string so that could be in the URL, the parameters, the headers and so on. For instance, if there is anywhere where we might otherwise copy in a board id, we can use the variable name instead and it will look like this.")) main.append(addSyntax("{{BOARD_ID}}")) main.append(addParagraph("To demonstrate this, I will start with a new request and type")) main.append(addSyntax("{{")) main.append(addParagraph("This brings up a list of suggestions as shown in figure 58.")) main.append(addImageWithCaption("images/suggested_variables.png", "Figure 58 - The list of suggested variables.")) main.append(addParagraph("Notice that our environment varialbes are all shown here and there are a couple of system variables displayed as well. I will select the BASE_URL and the BOARDS_PATH variables so the URL looks like this.")) main.append(addSyntax("{{BASE_URL}}{{BOARDS_PATH}}")) main.append(addParagraph("You may want to refer back to the image in figure 57 which shows that the current values for these variables are http://localhost:3000 and /api/boards respectively so putting them together like this is the equivalent of")) main.append(addSyntax("http://localhost:3000/api/boards")) main.append(addParagraph("Indeed, if we send the request, we get exactly the same results. One interesting thing I noticed here is that if you select the URL and right click, you can select Set: Local and then select one of the existing variables to set the URL as its value. In my case, I don't have a variable I would want to hold this value, but it's a useful option. Hovering over the URL also causes a box to appear labelled Set as variable as shown in figure 59.")) main.append(addImageWithCaption("images/set_as_variable.png", "Figure 59 - The set as variable option.")) main.append(addParagraph("We can expand the option by clicking on Set as variable to show the expanded options shown in figure 60 (note that the breadcrumbs here lead to options to encode or unencode the URI).")) main.append(addImageWithCaption("images/set_as_variable1.png", "Figure 60 - The set as variable option expanded.")) main.append(addParagraph("From here, we can set the value of one of our existing variables with this URL or create a new variable to hold the value. I don't really need to do this but in order to see how the option works, I have selected Set as new variable and given it the name GET_BOARDS_LIST. Note that the value can be edited here so I could use this to set the value of BASE_URL by simply removing the part of the value I don't need.")) main.append(addParagraph("In order to complete the process, I need to select a scope and there are three options for this, two of which we have seen already. There are")) main.append(addInsetList(["Global", "Active Environment: Local", "Collection: Postman Sample API"])) main.append(addParagraph("Bear in mind the fact that I only do have one collection and one environment, I would expect that other options would appear here (for example, inactive environments) if these were available so I guess that these should be thought of as three types of option rather than just three options. In this case, I will select the active environment and click Set as variable.")) main.append(addParagraph("Clicking Environment quick look again shows me that this new variable is now present in the environment and the value has been set as both the initial and current value. Recall that we can edit the value here by clicking the edit icon but something that wasn't apparent when we saw this earlier is that you can only edit the current value so you would need to go into the environment editor if you want to also change the initial value using the Persist All option (remembering to deselect any variables you don't want to include in that and reselecting them afterwards.)")) main.append(addParagraph("That done, we could now, if we wanted, specify our request as")) main.append(addSyntax("{{GET_BOARDS_LIST}}")) main.append(addParagraph("and again, this returns the list of boards just as the original request did.")) main.append(addParagraph("As you might expect, we can mix variable names with plain text and we will demonstrate this by creating a new request that returns a specific board. To do this, we will use the BASE_URL variable, specify the API route for boards - that is /api/boards - and append our baprd id onto the end of the request and this gives us")) main.append(addSyntax("{{BASE_URL}}/api/boards/{{BOARD_ID}}")) main.append(addParagraph("If we send that request, we do indeed see that specific board being returned.")) main.append(addParagraph("Before we move on, I will save this request with the name Board details in the collection, Postman Sample API and put it in to the Boards folder. To keep things tidy, I want to place it right after the previously saved Get baords list request in order to keep my get requests together so after saving it, in the Collections panel (if it isn't open, click on Collections in the left-hand panel and if necessary, expand the Boards folder) I will drag it and drop it in the desired position.")) main.append(addHeader("Authorization with Variables")) main.append(addParagraph("I have called up my Authorization request from the Postman Sample API collection and resent it, generating a new token. I could update the authorization at the collection level with the new token, but instead, I will insert the variable, {{BEARER_TOKEN}}. Remember that we set this variable up without giving it an initial or a current value so we can change that now by clicking on Environment quick look, selecting the edit icon next to the variable name and pasting in the new value. Now, if we need to refresh the token, we can just generate a new key and use it to update the value of the BEARER_KEY variable so in all likelihood, we will never need to edit the request itself again!")) main.append(addParagraph("Creating that variable as we did, without initially setting any value for it, may have seemed like a strange thing to do. However, it can be quite useful if we know that there is a variable we are going to need at some point but we don't yet have a value for it. In a real-life example, it is possible that we would create the variable and use it in requests before we had generated a token (although not, of course, before running a request that requires that token). Having the uninitialized variable there acts as a reminder that we need to get a token and that we need to update the variable's value with it. This is probably a good practice since it means that we are never having to use an actual token in our requests so it gives us a little more security.")) main.append(addHeader("Variable Shortcuts")) main.append(addParagraph("We used the Authorization request to generate a token which we, in turn, used to update the value of the BEARER_TOKEN variable. Using a returned value to set the value of a variable is such a common practice that Postman provides a nice and easy shortcut allowing us to do exactly that. Actually, I noticed this in an earlier section so this has already been mentioned, but I will cover it here so that it is in the correct context and we will also look at a practical example.")) main.append(addParagraph("Earlier, we ran a request to grab a board id and we then set up a variable to hold that value, BOARD_ID, which we used to get the details for that specific board. We would also like to be able to reuse that request to get the details for other boards as well, using the same variable modified to hold the id of the board we are interested in. In that example, we used it to retrieve the details for the first board, so this time we will use it to retrieve the details for the second board.")) main.append(addParagraph("To do this, I will resend the request that returned a list of board ids. I will then select the id for that second board in order to select it, right click and select Set: Local and then the board id. I can then rerun the Board details request without making any changes to it and it now returns the details for the second board. We can also click on Environment quick look to see the value there if we want to confirm that it has been updated.")) main.append(addHeader("Challenge: Body Variables")) main.append(addParagraph("For this challenge, we want to fix a potential security issue with our requests which is the use of plaintext username and password in our Authorization request. To fix thism we want to")) main.append(addInsetBulletList(["Define variables for our username and password.", "Set the current value for both.", "Reference those variables in the body of the Authorization request.", "Send the request to make sure we haven't broken it and that it will still return a valid key."])) main.append(addParagraph("To start with, I have clicked Environment quick look and then Edit to edit the environment. In the environment editor, I have added two new variables called USERNAME and USERPASSWORD without giving them any values as shown in figure 61.")) main.append(addImageWithCaption("images/challenge1.png", "Figure 61 - The environment editor showing the two uninitialised variables.")) main.append(addParagraph("Don't forget that you need to save the environment for the next step to work correctly. Open up the Authorization request, select the username, right click and then select Set: Local followed by the variable we want to initialise with this username, which not surprisingly is USERNAME. Using the same method, use the password to update the value of USER_PASSWORD and having done that, if you want to ensure that they have been updated, click on Environment quick look. Note that we now have too many variables to be displayed at the same time so we can either use the scroll bar to scroll down to the bottom of the variable list to see these new variables or we can click on Edit for the local environment editor and we can see the initialised variables there, as shown in figure 62.")) main.append(addImageWithCaption("images/challenge2.png", "Figure 62 - The environment editor showing the two variables initialised with the values from the body of the Authorization request.")) main.append("We also need to amend the Autorization request so that it uses the variables rather than actual values. Note that this is probably the only time when starting to type the variable name doesn't invoke a list of suggested variable names so we are going to ahve to type out these names in full (or copy and paste them) and this is show in figure 63.") main.append(addImageWithCaption("images/challenge2.png", "Figure 62 - The environment editor showing the two variables initialised with the values from the body of the Authorization request.")) main.append(addParagraph("We can then send the request again and we can now see that we have a new token as shown in figure 63 (notice that it is not the same token as the one shown in figure 62), so that confirms that the request using the variable names rather than actual values is still working. We didn't break anything!")) addSidebar("webdev");