# DevNet <details> <summary>1. Software Development and Design</summary> <details> <summary>1.1 Compare data formats (XML, JSON and YAML) </summary> ### XML Extensible Markup language is a method for wrapping data in tags. It is platform neutral and text based. Here is an example of what XML data looks like: ``` <?xml version="1.0" encoding="UTF-8"?> <!-- Router list --> <device> <hostname>Router1</hostname> <IPv4>192.168.1.1</IPv4> <online>true</online> </device> ``` The first line in an XML file is called the prologue. It contains the tag name of “xml” with the version and character encoding attributes. Normally the version is “1.0” and the character encoding is “UTF-8” or “UTF-16”. The second line in this example is an XML comment. Comments can go anywhere in the file. After the prologue and comment we have the XML document body. In the body, individual data elements are surrounded by tags. Tags are opened with <tag> and closed with </tag>. You can nest tags within tags as I have done in that example. XML tags are user-defined, meaning you can create tags for anything you need. Tag names are usually documented by the API provider so you can properly use them. XML is more verbose than JSON and can be more powerful. However, XML can be more difficult to parse than JSON with languages like python. ### JSON JavaScript Object Notation is a data format derived from object literals in JavaScript. Here is a sample JSON file: ``` { "device": { "hostname": "Router1", "ipv4": "192.168.1.1", "online": true } } ``` This is the same data as our XML example. The objects in JSON are key/value pairs. The objects can be many data types including numbers, strings, booleans or nulls. Objects can contain multiple key/value pairs separated by commas, like our device object in the example. JSON does not support comments unlike XML and YAML. Whitespace in JSON is insignificant and is mostly used for readability. ### YAML YAML is a acronym for “YAML Ain’t Markup Language” and is a superset of JSON. It was created for easier human readability. YAML parsers can parse JSON documents, but not vice-versa. Here is our example data in a YAML file: ``` --- device: # Router list hostname: Router1 ipv4: 192.168.1.1 online: true ... ``` YAML files open with three dashes and close with three dots. YAML data types include strings, numbers, booleans or nulls. Not that YAML strings can be left unquoted. Data types are key/value pairs separated by a colon. YAML makes it easier to represent more complex data like ordered lists. Here are 2 possible ways to create a list: ``` list1: - 1 - 2 - 3 list2: [1, 2, 3] ``` It is also possible to using a folding syntax to represent long strings. For example: ``` longstring: > This is a really long string that will look like one line once called longstring2: | This long string will have linebreaks as you see it here ``` Unlike JSON, whitespace in YAML is significant. Indentation indicates hierarchy. The number of spaces is arbitrary as long as it is consistent. It is also important to use spaces instead of tabs. Comments in YAML can be inserted anywhere and are preceded by a hash and a space. YAML has many features that make it easy to parse with python. Parsing will be discussed in the next exam objective. </details> <details> <summary>1.2 Parsing of common data formats into Python data structures</summary> ### Parsing XML in Python First, you’ll need to import the ElementTree helper library and the regular expression engine: ``` import xml.etree.ElementTree as ET import re ``` Then use the parse function from ElementTree to parse a file named “file.xml”. This will create a variable named xml that contains the data in a tree structure. Next, get the root of the tree using the getroot function. ``` xml = ET.parse("file.xml") root = xml.getroot() ``` Now you can match regular expressions in the file. ``` ns = re.match(r'{.*}', root.tag).group(0) device = root.find(f"{ns}device") ``` This searches the top level of the tree for the tag of <device>. If you are wondering why, I am using the same example XML file from this post. We can also search for the hostname of the device: ``` hostname = device.find(f"{ns}hostname") ``` Then we can use the print function to display that data. ``` print("The hostname is: %s" % hostname.text) ``` Parsing XML data isn’t the most straightforward task as compared to YAML & JSON. ### Parsing JSON in Python Parsing JSON in python is very important to understand if you are working with REST APIs (spoiler alert: we will be doing a lot of that). First things first, let import the JSON library: ``` import json ``` Next we’ll need to open the JSON file as a string, then use the load function to create a variable named data with JSON data: ``` json_file = open('file.json'), 'r') data = json.load(json_file) json_file.close() ``` With this, if we print the data variable here is what we’ll see: ``` python jsonparse.py {'device': {'hostname': 'Router1', 'ipv4': '192.168.1.1', 'online': True}} ``` We can tell this our JSON data in a python dictionary to do whatever you want with. If you want to print the hostname like we did in the XML example, do this: ``` print(data['device']['hostname'] ``` ### Parsing YAML in Python Like our previous example, we’re going to import the PyYAML library, open the file as a string, then pass it as yaml data to a variable named data: ``` import yaml yaml_file = open('file.yaml', 'r') data = yaml.safe_load(yaml_file) ``` If we print the data variable we will see the following: ``` python yamlparse.py {'device': {'hostname': 'Router1', 'ipv4': '192.168.1.1', 'online': True}} ``` As you can tell, just like the JSON example, the data is in a nested python dictionary. </details> <details> <summary>1.3 Concept of Test-driven development </summary> ### Test-driven Development Test-Driven-Development (TDD) is an approach to development where coding and testing are tightly interwoven. This is done by writing test code before writing the application code. The pattern of TDD is as follows: - Create a new test: This test should capture a requirement of the application code we are going to write. - Run tests: If there are any failures, correct the tests. Failures here are acceptable. - Write code to pass the new test: Add nothing more to the application code except what is required to pass the test. - Run tests: If any tests fail, fix the application code and re-run tests. - Refactor and improve code: Re-run tests each time the application code is improved and fix the code for each failure. The goal with TDD is that your are constantly testing as your are developing the application. In theory, this should result in high test coverage and that the application doesn’t have any failures at any point in time. TDD requires developers to constantly think about how to capture requirements in tests. It also helps to clarify what the code needs to do. It also means that the code will have to be highly-testable. An example is code with many functions that can be tested individually. </details> <details> <summary>1.4 Compare software development methods (agile, lean and waterfall) </summary> ### AGILE Agile is a flexible development methodology that was first officially described in the Agile Manifesto. The manifesto described the values as: - Individuals and interactions over processes and tools - Working software over comprehensive documentation - Customer collaboration over contract negotiation - Responding to change over following a plan The manifesto lists the following principles to accomplish these values: 1. Customer Focus 2. Embrace change and adapt 3. Frequent deliver of working software 4. Collaboration 5. Motivated teams 6. Face-to-face conversations 7. Working software 8. Work at a sustainable pace 9. Agile environment 10. Simplicity 11. Self-organizing teams 12. Continuous improvement You’ll notice that none of these things really describe how Agile works. That’s where Agile Scrum and Lean come in. I’ll be describing Lean in the next section, so let’s focus on Scrum first. Agile scrum focuses on small, self-organizing teams that meet daily and work in sprints. Sprints are quick iterations of the development life cycle. Usually sprints are between 2-4 weeks where a team takes on a user story and develops working software. User stories are small tasks that make up a feature. All of the features are put in a prioritized list known as the backlog. Scrum teams should not be larger than 10 people, but they should be large enough to complete a user story within a sprint. Every day the team will meet for a daily stand up meeting. These meetings should be short (less than 15 minutes) and take place at the same time every day. The goal of the standup meetings is to keep the team in sync with what has been done and what needs to be done. ### Lean Based on Lean Manufacturing, Lean focuses on minimizing waste and maximizing value to the customer. Lean has the following principles: - Eliminate waste: Waste is anything that does not add value for the customer. There are seven types of waste: 1. Partially done work 2. Extra processes 3. Extra features 4. Task switching 5. Wating 6. Motion 7. Defects - Amplify learning with short sprints: By having shorter sprints, developers can learn faster, customers can give feedback sooner, and features can be adjusted to bring value. - Decide as late as possible: It’s better to decide with all of the facts rather than speculation. When a decision is not yet made, the software can be designed to be more flexible, which helps in the future if requirements change. - Deliver as fast as possible: Enables customers to give feedback, gives customers features they need now, doesn’t allow customers to change their mind, makes decisions happen faster, produces less waste. - Empower the team: Allows everyone to make decisions in their areas of expertise. Increases efficiency by removing micromanagement. - Build integrity in: Software that addresses exactly what the customer needs, therefore maintaining it’s value. Optimize the whole: Each team member must step back and see the big picture. This ensures everyone considers the ramifications of their decisions. ### Waterfall With waterfall, the process can only go in one direction, never backwards. This means that each phase must be completed before moving on to the next. Here are the phases of the waterfall method: - System & Software Requirements: Defines what the application should do written down in a requirements document. - Analysis: The system is analyzed to generate the models and logic to be used in the application. - Program Design: This covers technical design requirements like programming language, services, data, etc. - Coding: This is where the source code is written. - Testing: Testers will discover and report any issues found. Although you can’t “go backwards” in waterfall, this stage can cause a “necessary repeat” of the coding phase. - Operations: This covers the deployment of the application, support and maintenance. </details> <details> <summary>1.5 Benefits of organising code into methods/functions, classes and modules</summary> ### Methods/Functions These are blocks of code that perform a task when called. If you are going to use the same code multiple times it is a good idea to encapsulate it into a method or function. They can be used as many times as you would like. Methods and functions are useful for clarity, testability, and re-use. If used correctly, they will simplify code. In python, you can create a function like this: ``` def sampleFunction(): #here is some code ``` You can then call the function in your code: ``` sampleFunction() ``` Methods and functions can have arguments passed to them. The method/function has to accept these arguments as parameters. These parameters can be any type of data, and if there are multiple parameters, each can be different. Here’s an example of using arguments and parameters: ``` def sampleFunction(parameter1, parameter2): #parameter1 is a string #parameter2 is an integer #calling the function sampleFunction("This string", 55) ``` When using methods & functions, you can return a value after running. You use the return statement for this. When you call the function in the code, you can pass the returned value into a variable. Here is an example with some integers: ``` def sumOf(num1, num2): sum = num1 + num2 return sum #Calling the function resultVariable = sumOf(2,4) ``` In this example, we are passing 2 and 4 as parameters into our function which adds them together and returns the result. This means that the value of resultVariable will be 6. Methods and functions are similar. The reason for the two names is that functions are standalone code blocks, which methods are associated with an object. Object-oriented programming will be explained more next. ### Classes Classes are a way to bundle data and functionality. Each class defines a new object type, allowing new instances of that type to be made. In simpler terms, classes are essentially a template to create objects. The important thing to note with classes is that python is an object-oriented programming language. This means python focuses on creating reusable code. When working with complex projects, classes will help you reuse code, which then will allow you to better scale and maintain your code. ### Modules Modules are independent blocks of code that can be reused. Usually these are used to divide large projects into smaller parts. This helps make code easier to read. Modules are packaged into a single file that can work independently or integrate with other code. Let’s look at an example module. Basically modules consist of one or more functions, so our example with have 2 functions and be named testModule.py: ``` def sumOf(num1,num2): sum = num1 + num2 return sum def productOf(num1,num2): product = num1 * num2 return product ``` In order to use the module in an application, do the following: ``` import testModule testModule.sumOf(2,4) testModule.productOf(5,8) ``` You can see the module acts like a library that needs to be imported into the application. Then you can call the individual functions. </details> <details> <summary>1.6 Advantages of common design patterns</summary> ### MVC – Model-View-Controller The MVC design pattern’s goal is to simplify development of applications that depend on a GUI. MVC abstracts the code into three components that communicate in one direction. Components: - Model – The model is the data structure of the application. It gets input from the controller. - View – The visual representation of the data. It gets input from the model. - Controller – Takes user input and changes it to the format for the model. The flow of MVC looks like this: - User provides input - Controller takes input and manipulates the data - Controller sends data to the model - Model processes data and sends selected data to the view - View displays selected data to the user The MVC design is commonly used in web apps or user interfaces. The benefit is that the components can be built in parallel and the components to not need to be aware of the implementation within the other components. This is because they only need to be aware of the input/output interfaces of each other. ### Observer This design pattern is based on subscriptions and notifications. It allows observers (objects or subscribers) to receive events when there are changes to a object that is being observed. The benefit is that observers get real time data from the subject when any change happens. This is going to provide better performance than polling. </details> <details> <summary>1.7 Advantages of version control</summary> ### Version Control Version control is a way to manage changes to files and keep a history of those changes. Version control systems are responsible for storing the files in a repository. Within a repository, the “main” files are stored as the master set. In order to make changes, users copy the files locally and can make changes to their copy without impact other users or the master copy. Some advantages are: - Collaboration: Multiple people can work on the same file simultaneously. - Accountability/Visibility: You can see who made what changes and why. - Working in isolation: You can build new features without impacting the existing application. - Safety: Files are easily reverted if there is a mistake. - Work anywhere: Since you can copy the repository to any device. The most popular version control system today is Git. </details> <details> <summary>1.8 Git Operations </summary> #### 1.8.a Clone When you clone a repo it means to copy it to the local filesystem. This creates a working copy of the repo. The command is: ``git clone <repository> [target directory]`` Repository is the location of the repo. This can be a local repo, SSH or HTTP. Git also has it’s own transport. Target directory is optional. If you do not specify the target, the repo will be copied to the location where you executed the command. When you run this command, a .git folder will be created inside the new folder that contains the metadata of the repo. It will also duplicate the branch structure and enable tracking of each branch. #### 1.8.b Add/remove When you change a file in the local working directory, it has to be added to the staging area before it can be updated in the git repository. This includes untracked files that you want to be tracked and tracked files that you have changed. This is done with the git add command. You can add multiple files before a commit and only files specified are added to the staging area. ``git add <file path>`` To add multiple files: ``git add <file 1> <file 2> .... <file n>`` To all all changed files: ``git add .`` To remove files from the git repo, use: ``git rm <file path>`` Just like git add, you can do multiple files or all files in a directory. Note that git rm removes files from the staging area and the working directory. If you just want to remove files from being tracked by git, but don’t want to delete the file from the file system use the following: ``git rm --cached <file path>`` #### 1.8.c Commit Once files are in the staging area, you need to update the local repository with the changes. This is what a commit does. The command is simple: ``git commit`` This combines all of the changes into a single commit. However it does not update the remote repository. Usually you will add a comment to the commit to explain the reason for it. This looks like: ``git commit - m "<message>"`` If there are no pending changes in the staging area, the command will return a message saying that nothing will be updated. #### 1.8.d Push / pull Once we have added our changes to the staging area with add, then updated the local repo with commit, we can finally push those changes to the remote repository. This is done with the following: ``git push`` If there is a conflict the command will not be successful. You will need to resolve the conflicts before executing a successful push. If you are trying to update a specific branch, use the following: ``` git push origin <branch name> # or git push origin master ``` The pull command is used because your local repository does not automatically update when the remote repo is changed. In order to update the local copy use: ``git pull`` This updates the local repo with the latest commit, file histories, etc. If you are trying to update a specific branch, use: ``git pull origin <branch>`` #### 1.8.e Branch Branches allow users to work on code without affecting the main code in the repo. You can have multiple branches for a specific project. You can create a branch with the following: ``git branch <parent branch> <new branch name>`` Branches can be deleted with: ``git branch -d <branch name>`` To get a list of all the local branches, use: ``` git branch #or git branch --list ``` #### 1.8.f Merge and handling conflicts In order to changes from one branch into another, they need to be merged. When git merges a branch, it takes the changes from the source branch and applies it to the target branch. When the git algorithm is able to apply the changes/commits from the source to target automatically without conflicts it is known as a fast-forward merge. Conflicts are likely if the same file has been modified on multiple branches and then merged. In this case, git will not be able to automatically apply the changes and manual intervention is required in order to merge the branches. To perform a merge, use the following: ``git merge <branch name>`` where <branch name> is the source branch that you are merging into the current branch (target branch). When using the git merge command, the target branch must be the current branch/repo. To merge more than one branch into the current branch, use: ``git merge <branch 1> <branch 2> ... <branch n>`` #### 1.8.g diff The diff command in git is a file difference tool. Both files do not need to be tracked by git in order to use this command. There are many things you can do with the git diff command. For example, you can show changes between the version of the file in the working directory and the last commit: ``git diff <file path>`` You can show changes between the working version and a specific commit: ``git diff <commit id> <file path>`` Show the changes between two local files: ``git diff <file path 1> <file path 2>`` </details> </details> <details> <summary>2. Understanding and Using API(s)</summary> <details> <summary>2.1 Construct a REST API request to accomplish a task</summary> REST API requests are HTTP requests that follow REST principles. They are a way for a client to ask the server to perform a function. REST API request are made up of 4 components: - Uniform Resource Identifier (URI) -- URI can also be referred to as URL. -- Identifies the resource and is made up of multiple parts. -- In the example: https://localhost:8000/v1/devices/?q=Router --- https: is the scheme ---- Defines the protocol to be used --- //localhost:8000 is the authority ---- Destination that consists of host and port --- /v1/devices is the path ---- Represents the location of the resource to be manipulated. Can consist of multiple segments. --- /?q=Router is the query ----Optional, provides additional details for the scope. - HTTP Method -- REST APIs use standard HTTP methods. The documentation will tell you what is supported for each request, but they usually are as follows: --- POST – Create a new resource --- GET – Retrieve a resource --- PUT – Replace or update a resource --- PATCH – Update some details about a resource --- DELETE – Remove a resource - Header -- Standard HTTP headers are used for REST APIs. They are optional and formatted as key/value pairs. -- There are 2 types of headers: --- Request Headers – include additional information that doesn’t relate to the content of the message, like authorization. --- Entity Headers – include information that describes the content of the message, like the content-type. - Body -- Contains the data of the message. -- Optional depending on the HTTP method, but if provided the data type must be specified in the header (content-type). </details> <details> <summary>2.2 Describe common usage patterns related to webhooks</summary> Webhooks are HTTP POST messages from a server that notify an application when an event occurs. The application registers with a webhook provider by giving a URI that will be called by the server when an event occurs. This can be thought of as a reverse API. The URI usually represents the API for the application. When the webhook is triggered, the server becomes the caller and makes a request to the provided URI. In order to receive a notification from a webhook provider, your application must meet the following: - It must be running at all times - The application must register a URI on the webhook provider Working with webhooks usually involve working with a third party, luckily there are many webhook testers online that can be useful during the implementation process. </details> <details> <summary>2.3 Identify the constraints when consuming API(s</summary> The REST architectural style devices 6 constraints which make an API RESTful: - Client-Server -- The client and server should be independent of each other - Stateless -- The server will not store anything about the last HTTP request from a client. Each request will be treated as new. - Cache -- The server must declare if the resource is cacheable or not. If so, the client can use the cache for later requests. - Uniform Interface -- Four guidelines: --- Resource-based – Individual resources are identified in requests. --- Manipulation of Resources through representations – The client receives a representation of the resource from the server. This representation must contain enough data or metadata for the client to be able to manipulate the resource. --- Self-descriptive Messages – Each message must contain all of the info for the client to process the message. --- Hypermedia as the Engine of Application State – Data sent by the server must include additional actions and resources available for the client to access supplemental info about the resource. - Layered System -- The application is made up of hierarchical layers that can only provide services to the layer above it and consume services from the layer below it. - Code-On-Demand (optional) -- Information returned can include executable code, but is optional due to security considerations. </details> <details> <summary>2.4 Common HTTP Response Codes</summary> HTTP Response codes are always three digits. They fall into the following categories: - 1xx – Informational - 2xx – Success - 3xx – Redirection - 4xx – Client Error - 5xx – Server Error IANA has an official registry of HTTP status codes, but for our purposes we’ll go over the common codes that REST API’s use. - 200 – “OK” – Request was successful - 201 – “Created” – Request was fulfilled and the requested resource was created - 202 – “Accepted” – Request has been accepted for processing, but not complete - 400 – “Bad Request” – Request will not be processed due to an error with the request - 401 – “Unauthorized” – Request does not have valid credentials - 403 – “Forbidden” – Request was understood but rejected by the server - 404 – “Not Found” – The resource path of the request was not found on the server - 500 – “Internal Server Error” – Request cannot be fulfilled due to a server error - 503 – “Service Unavailable” – Currently the server cannot handle the request </details> <details> <summary>2.5 Troubleshoot a problem given the HTTP code, request and API documentation</summary> This objective is kind of a tough one to make a blog post for since there are a lot of different variables and all APIs are different. That being said, there are some things we can look for. For any request, perform the following: - Check the response code. - Check the response body – Most of the time you can find what is wrong in the message along with the status code. - If you can’t find the issue, use the status code and API documentation to understand the error. If the error is client side (4xx), you can check a few things: 1. Bad syntax in the request 2. Invalid credentials 3. Access is not allowed 4. Wrong method in request 5. Unsupported media type (i.e. XML instead of JSON) If the error is 500 (Internal Server Error) or 501 (Not Implemented), you should check the API reference guide to make sure the request is valid. For any other 5xx error, check with the API server admin to resolve the issue. </details> <details> <summary>2.6 Identify the parts of an HTTP response (response code, headers, body)</summary> #### Headers HTTP response headers are similar to HTTP request headers. They are optional and use key/value pairs separated by a colon. There are two types: - Response headers – Contain additional info that doesn’t relate to the content of the message. For example, “Set-Cookie”. - Entity headers – Contain additional info that describes the content, for example “Content-Type” #### Body The body contains the data requested by the client and is also optional. If data is included, the type is specified in the header. If the request is unsuccessful, the body may provide additional information as to why, or how to resolve the issue. If a large amount of data needs to be sent, there are two ways APIs can handle it. One is splitting up the data into chunks, known as pagination. How pagination is handled is described in the documentation for the specific API. The other way to send large amounts of data is with compression. Compression can be requested in the API request with the “Accept-Encoding” field in the header. The possible values are: - gzip - compress - deflate - br - identity - * </details> <details> <summary>2.7 Utilize common API authentication mechanisms: basic, custom token, and API keys</summary> #### Basic Basic authentication transmits credentials as username/password pairs separated by a colon. It looks like this in the request: ``Authorization: Basic <user>:<pass>`` These passwords are in plain text and are very insecure unless paired with HTTPS. #### Custom Token Token auth uses the Bearer HTTP authentication scheme. It is more secure than basic authentication and is used with OAuth and SSO. It uses a token generated by an authentication server. It looks like this in the request: ``Authorization: Bearer <token>`` It should also be used with HTTPS instead of HTTP. #### API Keys API keys are generated by a server and assigned to an individual user. To obtain the key, users log into the server using their credentials and get the assigned key. These keys are assigned once and are not regenerated. The user then has to use the key in every request. Just like Basic & Bearer, it should be paired with HTTPS. There are two types of API keys: - Public - Private A public key can be shared, but a private key must not be shared since it is paired to a user’s credentials. For public keys, you can provide it in a query: ``GET https://localhost:8000/v1/devices?API_KEY=YOUR_KEY`` For private keys, there are a few options for putting them in the header: ``` Authorization: <API_KEY> #OR Authorization: APIKEY <API_KEY> #OR APIKEY: <API_KEY> ``` You can also put keys in the body of the request: ``` { API_KEY: <API_KEY> } ``` Lastly, you can use a cookie: ``Cookie: API_KEY = <API_KEY>`` </details> <details> <summary>2.8 Compare common API Styles(REST, RPC, Synchronous, Asynchronous)</summary> #### RPC Remote Procedure Call is a request-response model that enables a client to make a procedure call to another application acting as a server. The client is usually unaware that the request is being executed remotely because the request is made to a layer that hides those details. The most common usage is when a client makes a synchronous request to a server and is blocked while the server processes it. When the server has processed the request, it sends a response back to the client, and the client unblocks the request. RPC can be applies to XML-RPC, JSON-RPC, NFS and SOAP. #### Synchronous Synchronous APIs respond to a request directly. APIs are usually designed this way when the data is readily available. This way the server can instantly respond with the data. This is common if the data is in a database or internal memory. From the client perspective, the application must wait for the response from the server before performing any other tasks. However, if the API is designed correctly, the data should be received immediately. #### Asynchronous Asynchronous APIs provide a response to the client that the request has been received, but the response doesn’t have the actual data. The server must process the request, then sends a notification to the client or sends a callback with the requested data. APIs are usually designed this way if the data isn’t readily available, for example if it’s on a remote service that the service has to request the data from. In this case the server cannot guarantee the data will be returned immediately. Asynchronous APIs allow the application to continue execution without being locked until the server processes the request. In order to use these APIs, the client will need to have either a listener or possibly a polling mechanism to find out the status of a request or receive the data. </details> <details> <summary>Construct a Python script that calls a REST API using the requests library</summary> The first thing in your script that you will have to do is import the requests library. Then you will want to create a variable with the URL of the API. There are ways to use variables within the URL, but we’ll focus on a simple script for right now. ``` import requests url = "https://localhost:8000/api/v1/devices" ``` Now that we are set up, we can start making requests with our code. The first example is making a GET request. What we’ll do here is make a GET request and assign that response to a variable. We can then use python to do whatever we want with the data in that variable. In this case we’ll just print a few of the available functions. I’ll execute the code in IDLE so we can see our output. ``` >>>response = requests.get(url) >>>response.encoding = 'utf-8' #this is optional, and converts the raw bytes into a string >>>reponse.status_code 200 >>>response.text '{"current_user_url": "https://localhost:8000/api/v1/devices"... #OUTPUT OMITTED FOR BREVITY} >>>response.headers['content-type'] 'application/json; charset=utf8' ``` The other HTTP methods are similar. Here are examples of using other methods: ``` requests.post(url, data={'key': 'value'}) requests.put(url, data={'key': 'value'}) requests.delete(url) requests.patch((url, data={'key': 'value'}) ``` For each of these methods you can inspect them the same way we did with GET. I also want to show that there is another method, called request. In this method you specify the method within the request. It may seem weird, but think about using this in a script or application where you want to set the method as a variable instead of hard coding it. I won’t go into too much detail, but here is an example. In this script we will set variables for the header, then do a GET request from the API. ``` import requests url = "https://localhost:8000/api/v1/devices" payload = {} headers = { 'Content-Type': 'application/json', 'X-Auth-Token': 'TOKEN-STRING' } response = requests.request("GET", url, headers=headers, data = payload) print(response.text.encode('utf8')) ``` </details> </details>