One place for hosting & domains

      How To Create an OAuth App with the Linode Python API Library


      Updated by Linode

      Contributed by

      Linode

      Linode supports the OAuth 2 authorization protocol. OAuth 2 allows a user to safely grant a third-party app permission to act on their behalf. This means that a user could authorize an app to access data and / or make changes to their Linode account and services that are exposed by the Linode APIv4. For example, an app could create or destroy Linodes, manage a NodeBalancer, or alter a domain.

      This guide will show you how to create a simple OAuth application using Flask and the Linode Python API library. This app allows a user to log in with their Linode account and create a Linode with a StackScript. The complete code for this example is available in the Linode APIv4 Python library example repository.

      Before You Begin

      1. Normally, in order to create an OAuth app with Linode your server must have HTTPS enabled. The only exceptions to this rule are localhost addresses, which can use HTTP. As this guide is just a primer and is not intended to supply production ready code, we will be working with a local workstation, using localhost. If you choose to create an app for production, you will need to generate SSL certificates for HTTPS access.

      2. Ensure that Python 3 is installed on your workstation.

      Obtaining a Client ID and a Client Secret

      In order for Linode to verify the identity of your app, called a client, you will need to generate a set of credentials, specifically a client ID and a client secret.

      1. Log in to the Linode Cloud Manager and navigate to your Account Profile.

        OAuth Account Profile

      2. From there, click on the My Apps tab and select Create My App. You will be prompted to supply a label for your app and a callback URL. We will discuss the role of the callback URL in depth later in this guide. For now you can supply the following URL:

        http://localhost:5000/auth_callback
        

        Leave Public unchecked and click Submit.

        OAuth Account Profile

      3. A window will appear with your client secret. Copy this down somewhere secure, as once you exit this window you will not be able to retrieve the client secret again, and will be forced to generate a new one.

        OAuth Account Profile

      4. Once you exit the client secret window your app will appear as part of a list of apps. Note your client ID, as this is the last piece of information you will need to verify your app’s identity.

        OAuth Account Profile

      In summary, you should have these three bits of information, with values similar to the ones provided here:

      OAuth 2 Authentication Exchange

      The OAuth 2 workflow is a series of exchanges between your third-party app and Linode. Below is an explanation of these exchanges.

      1. The end user visits your client application’s website and attempts to login.
      2. Your client application redirects the end user to the authentication server (https://login.linode.com) with your client application’s client ID and requested OAuth scopes, which appear in the URL of the login page.
      3. The end user inputs their username and password to the authorization server and authorizes the login.
      4. The authorization server redirects the end user back to your client application with a temporary authorization code (sometimes called an exchange code) in the URL.
      5. The client application issues a POST request to the authentication server containing the authorization code and the client application’s client secret.
      6. The authentication server responds to the client application with a newly issued OAuth access token.

      In the following sections you will write the code to perform each one of these steps, using the Linode Python API library.

      Setup Your Development Environment

      1. Create a project folder and move into that folder.

        mkdir ~/linode-oauth-project && cd ~/linode-oauth-project
        
      2. For this project, you will need to use pip to download and install the required Python libraries. Install pip if you do not already have it:

        apt install python-pip
        
      3. Install the required Python libraries:

        pip install flask flask-session linode_api4
        

      Configure Your App

      In a text editor, create a file named config.py. Add the following variables and values, being sure to change the values to your own.

      The StackScript used in this example is for demo purposes. To explore other available StackScripts, visit the Linode StackScript Library. Note that the stackscript_id does not have quotation marks around it. The secret key is used for serializing session data, and should be a value only you know.

      config.py
      1
      2
      3
      4
      5
      
      client_id = 'ce571a8cdad1ba4a0a7d'
      client_secret = 'fab8e2222e83b9b2f50a76012122ec20a5acb005ed088f3fccda2c9c2c4e1cbd'
      stackscript_id = 320826
      application_name = 'my-application-name'
      secret_key = 'my-secret-key'

      Author an OAuth2 App

      In this section, you will write the code for the app.

      Include Imports

      Ensure you are in the linode-oauth-project directory and create and open a file called app.py in the text editor of your choice. Include the following libraries:

      app.py
      1
      2
      3
      4
      5
      6
      
      import re
      from flask import Flask, redirect, request, render_template, session, send_from_directory
      from flask_session import Session
      from linode_api4 import (LinodeClient, LinodeLoginClient, StackScript, Image, Region, Type, OAuthScopes)
      
      import config

      Set Up Flask and Session Key

      Copy in the following code to set up Flask and the session secret key:

      app.py
      1
      2
      3
      4
      
      ...
      
      app=Flask(__name__)
      app.config['SECRET_KEY'] = config.secret_key

      Create a Function to Return the Linode Login Client

      In app.py add the following function to return the LinodeLoginClient class. The LinodeLoginClient class is the library’s OAuth interface. Note that we are passing the client_id and client_secret parameters from our config.py file to the class:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      
      ...
      
      def get_login_client():
          return LinodeLoginClient(config.client_id, config.client_secret)

      Create an Index Route

      In Flask you can create HTTP endpoints with routes. The index route, defined in the code below at the document root /, will be the route the user will see when they navigate to http://localhost:5000/. This route will be responsible for displaying the available Linode plan types, the available regions, and the StackScript-compatible images that a user will choose from when creating their new Linode.

      To query a list of available plan types and regions you can use the LinodeClient class, which is an interface for Linode’s APIv4. Viewing the Linode plan types and regions does not require any sort of authorization, so you can provide a dummy value of no-token to instantiate the class:

      ~/linode-oauth-project/app.py
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      
      ...
      
      @app.route('/')
      def index():
          client = LinodeClient('no-token')
          types = client.linode.types(Type.label.contains("Linode"))
          regions = client.regions()
          stackscript = StackScript(client, config.stackscript_id)
          return render_template('configure.html',
              types=types,
              regions=regions,
              application_name=config.application_name,
              stackscript=stackscript
          )

      It is important to note that the two API queries in the above code are slightly different from one another. The client.regions method is a top-level method, just as it appears in the Linode API. The client.linode.types method, on the other hand, is part of the Linode group, which is a collection of methods that deal with Linodes. Again, this is because Linode endpoints are grouped that way in the API. Some methods in the Linode Python library are top level, such as domain_create, while others, like networking.ip_assign, are part of a group. For more information on the top-level methods and groupings, consult the library documentation.

      In addition to querying the API, the above route also renders the configure.html template by passing it the types, regions, application name, and StackScript object. The StackScript object contains a list of StackScript compatible images. We will cover templating in a later section.

      Create a Login Route

      Next, create a login route in app.py. This route will perform two functions. First, it will serialize the user’s plan type, region, and image selections into the session.

      Second, this route will redirect the user to Linode’s login page where they will be prompted to authorize your client app and the scopes you have requested for it. Scopes are sets of permissions that define the access level of your client app. For instance, to create a Linode, your end user must authorize the OAuthScopes.Linodes.create scope.

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      ...
      
      @app.route('/', methods=["POST"])
      def start_auth():
          login_client = get_login_client()
          session['dc'] = request.form['region']
          session['distro'] = request.form['distribution']
          session['type'] = request.form['type']
          return redirect(login_client.generate_login_url(scopes=OAuthScopes.Linodes.create))

      When the user returns to your app from the Linode login page, they will be directed to the callback URL.

      Note

      Below is a list of available scopes:

      • OAuthScopes.Linodes
      • OAuthScopes.Domains
      • OAuthScopes.StackScripts
      • OAuthScopes.Users
      • OAuthScopes.NodeBalancers
      • OAuthScopes.Tokens
      • OAuthScopes.IPs
      • OAuthScopes.Tickets
      • OAuthScopes.Clients
      • OAuthScopes.Account
      • OAuthScopes.Events
      • OAuthScopes.Volumes

      Each scope is broken into five permissions: view, create, modify, delete, and all. The all permission encompasses the other four permissions.

      Manage the OAuth 2 Callback URL

      The OAuth 2 callback URL has two main responsibilities. Its first responsibility is to help prove the identity of the client application. When a user attempts to log in to Linode through OAuth, instead of redirecting the user back to the page they came from, Linode’s OAuth implementation matches the client ID to the callback URL you have registered with your app on Linode’s system. This ensures that a nefarious third party can’t just steal the client ID, which is public, and attempt to authorize their own app with it.

      The callback URL’s second responsibility is to kick off the process of exchanging an authorization code for an access token. This second process is done over POST, and so it doesn’t require the user to physically leave the page they are returned to after they log in to Linode. Now you will write the code that satisfies this second responsibility.

      In app.py, add the following lines:

      ~/linode-oauth-project/app.py
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      ...
      
      @app.route('/auth_callback')
      def auth_callback():
          code = request.args.get('code')
          login_client = get_login_client()
          token, scopes, _, _ = login_client.finish_oauth(code)
      
          # ensure we have sufficient scopes
          if not OAuthScopes.Linodes.create in scopes:
              return render_template('error.html', error='Insufficient scopes granted to deploy {}'
                      .format(config.application_name))
      
          (linode, password) = make_instance(token, session['type'], session['dc'], session['distro'])
      
          get_login_client().expire_token(token)
          return render_template('success.html',
              password=password,
              linode=linode,
              application_name=config.application_name
          )

      Let’s take a look at what each of the parts of this section does.

      First, a route is defined for the callback with @app.route(), then a function called auth_callback is defined that will run whenever this route is accessed:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      
      ...
      @app.route('/auth_callback')
      def auth_callback():
      ...

      When the user is returned to the callback URL, an authorization code is appended to the URL. The variable code is set to retrieve this value from the URL’s request arguments:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          code = request.args.get('code')
      ...

      Then you retrieve an instance of the LinodeLoginClient class:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          login_client = get_login_client()
      ...

      Once you have the LinodeLoginClient class, you can pass the authorization code to the finish_oauth method, which is a helper method that will manage the authorization code to OAuth token exchange. This method returns an OAuth token, and the scopes the user has agreed upon.

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          token, scopes, _, _ = login_client.finish_oauth(code)
      ...

      The next section compares the scopes your app requested from the user to the scopes returned by Linode’s OAuth login page. If the returned scopes do not include the correct scopes, in this case the OAuthScopes.Linode.create scope, then an error template is rendered and an error message is displayed:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      5
      6
      
      ...
          # ensure we have sufficient scopes
          if not OAuthScopes.Linodes.create in scopes:
              return render_template('error.html', error='Insufficient scopes granted to deploy {}'
                      .format(config.application_name))
      ...

      Once your app has determined that it has the correct permissions, it creates the Linode using the Linode plan type, the region, and the image that the app serialized into session storage. You will create the make_instance function in the next step. The make_instance function returns the linode object, which contains the Linode’s label, group, and IP address, and the function also returns a randomly generated password:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      ...
          (linode, password) = make_instance(token, session['type'], session['dc'], session['distro'])
      ...

      Once the Linode has been created, the app expires the OAuth access token. Expiring tokens after use is a strong security measure but if your app is performing many actions on behalf of the user, you might find that time-based expiration scheme is more suitable to your needs. The app then renders the success template by passing it the linode object, the password, and application name:

      ~/linode-oauth-project/app.py
      1
      2
      3
      4
      5
      6
      7
      
      ...
          get_login_client().expire_token(token)
          return render_template('success.html',
              password=password,
              linode=linode,
              application_name=config.application_name
          )

      Create a Function to Deploy a Linode

      Now, create the make_instance function that you referenced above:

      ~/linode-oauth-project/app.py
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      
      ...
      
      def make_instance(token, type_id, region_id, distribution_id):
          client = LinodeClient('{}'.format(token))
          stackscript = StackScript(client, config.stackscript_id)
          (linode, password) = client.linode.instance_create(type_id, region_id,
                  group=config.application_name,
                  image=distribution_id, stackscript=stackscript.id)
      
          if not linode:
              raise RuntimeError("it didn't work")
          return linode, password

      The make_instance function takes an OAuth access token, the type ID, the region ID, and the image (Linux distribution) ID as parameters. It creates an instance of the LinodeClient class, and unlike the instance of LinodeClient used earlier in the guide, this one requires an OAuth token because you will be using it to create a Linode. The function then creates a Linode using the linode.instance_create method, returning the linode object and the password.

      Finally, if there was an error with the creation of the Linode, the if not linode statement will raise a runtime error.

      Set the name Variable

      At the end of your app.py, paste in the following code to make sure you can run your app:

      ~/linode-oauth-project/app.py
      1
      2
      3
      
      if __name__ == '__main__':
          app.debug=True
          app.run()

      Create App Templates

      Now that you have written the backend code for your app, you’ll need to create a frontend user interface. Begin by creating a templates directory in your project directory and moving into it:

      mkdir ~/linode-oauth-project/templates && cd ~/linode-oauth-project/templates
      

      Using your preferred text editor, create and open base.html. This will be the base template from which your other templates will inherit their stylesheets and JavaScript files:

      ~/linode-oauth-project/templates/base.html
       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
      
      <html>
      <head>
          <title>Install On Linode</title>
          <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
              integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
          <style>
              body{
                  text-align: center;
                  background-color: #333333;
              }
              .form-group{
                  display: inline-block;
                  text-align: left;
                  width: 250px;
                  border: 1px solid #cccccc;
                  margin: 5px;
                  padding: 5px;
              }
              .form-group label{
                  color: #337ab7;
              }
              .form-group select{
                  font-size: 16px;
                  outline: none;
                  border: 0px solid #000000;
                  box-shadow: inset 0 1px 1px rgba(0,0,0,0);
                  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0);
              }
              .form-group select:focus{
                  box-shadow: inset 0 1px 1px rgba(0,0,0,0);
                  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0);
              }
              .btn-lg{
                  border-radius: 0px;
                  margin-top: 20px;
              }
              .row{
                  margin-bottom: 20px
              }
              .pop{
                  color: #337ab7;
                  font-weight: bold
              }
              code{
                  color: #337ab7;
                  background-color: #eeeeee
              }
              .boxy{
                  border: 1px solid #cccccc;
                  width: 400px;
                  background-color: #f9f9f9;
                  margin: auto;
                  padding: 10px;
              }
          </style>
      </head>
      <body>
          <div class='container' style='background-color: white; border-left: grey; border-right: grey; height: 100%; padding: 20px;'>
              {% block content %}
              {% endblock %}
          </div>
      
          <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
          <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
              integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
              crossorigin="anonymous"></script>
      </body>

      The important thing to note in the above template is the Jinja2 templating tags. They are:

      {% block content %}
      {% endblock %}
      

      As you will see, any template that extends the base.html template and includes code between the opening and closing content block, will render the code laid out by base.html.

      Create a file called configure.html, which will be the UI a user will see when they reach the document root endpoint (/). Copy in the following code:

      templates/configure.html
       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
      
      {% extends 'base.html' %}
      {% block content %}
          <form method="POST">
              <div class='row'>
                  <h1>Deploy <span style="color: #337ab7;">{{application_name}}</span> to a Linode</h1>
                  <p>
                      This will create a brand new Linode running {{application_name}} on your
                      account and give you the credentials.
                  </p>
              </div>
              <div class='row'>
                  <div class='form-group'>
                      <label for='type'>Type</label>
                      <select name='type'i id='type' class='form-control'
                          onblur="blurring(this)" onfocus="focusing(this)">
                          {% for s in types %}
                              <option value="{{s.id}}">{{s.label}}</option>
                          {% endfor %}
                      </select>
                  </div>
                  <div class='form-group'>
                      <label for='region'>Region</label>
                      <select name='region' id='region' class='form-control'
                          onblur="blurring(this)" onfocus="focusing(this)">
                          {% for o in regions %}
                              <option value="{{o.id}}">{{o.id}}</option>
                          {% endfor %}
                      </select>
                  </div>
                  <div class='form-group'>
                      <label for='distribution'>Images</label>
                      <select name='distribution' id='distribution' class='form-control'
                          onblur="blurring(this)" onfocus="focusing(this)">
                          {% for d in stackscript.images %}
                              <option value="{{d.id.id}}">{{d.id.id}}</option>
                          {% endfor %}
                      </select>
                  </div>
              </div>
              <div class='row'>
                  <input type="submit" value="Deploy Linode" class='btn btn-primary btn-lg'/>
              </div>
          </form>
          <script>
              function focusing(ele){
                  ele.parentElement.style.borderColor = "#337ab7";
              }
              function blurring(ele){
                  ele.parentElement.style.borderColor = "#cccccc";
              }
          </script>
      {% endblock %}

      Here the template begins with two statements: {% extends 'base.html' %} and a {% block content %} statement. These two tags tell Jinja2 to extend the code within base.html, and to place everything within {% block content %} ... {% endblock %} in configure.html between the corresponding {% block content %} ... {% endblock %} tags in base.html.

      configure.html includes Jinja2 logic, with the inclusion of for statements like {% for o in regions %}. These statements are like for statements in other languages, and are used to iterate over an array or list. In this example, it is iterating over the regions that we passed to the template from the index route. configure.html also contains variables, which are denoted by double curly brackets: {{ s.id }}.

      Create another file called error.html. This will be the template that appears whenever there is an error in the Linode deployment. Copy in the following code:

      templates/error.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      {% extends 'base.html' %}
      {% block content %}
          <div class='row'>
              <h1 class="pop">Error</h1>
              <p>{{error}}</p>
          </div>
          <div class='row' style='margin-top: 20px'>
              <a href='/' class='btn btn-lg btn-default'>Try Again</a>
          </div>
      {% endblock %}

      This template works the same way that configure.html does, by extending base.html and providing its own content block.

      Lastly, create another file called success.html. This file follows the pattern set by configure.html and error.html, and will present the user with a confirmation message whenever a Linode is successfully created. This message includes the Linode’s label, group, IP address, and password:

      templates/success.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      
      {% extends 'base.html' %}
      {% block content %}
          <div class='row'>
              <h1>Success!</h1>
              <p>{{application_name}} has been deployed to <span class="pop">{{linode.label}}</span> in the {{linode.group}} group.</p>
          </div>
          <div class='row'>
              <div class='boxy'>
                  <p>You can access your Linode with the following command:</p>
                  <code>ssh root@{{linode.ipv4[0]}}</code>
                  <br />
                  <br />
                  <p>Your root password is:</p>
                  <code>{{password}}</code>
              </div>
          </div>
      {% endblock %}

      Run Your App

      You are now ready to run your app. Change back to your project’s main directory:

      cd ~/linode-oauth-project
      

      Run the app.py script:

      python3 app.py
      

      Open your browser to the following URL:

      http://localhost:5000/
      

      You should be greeted with your new app. Select a plan, a region, and an image to deploy a Linode using the Linode API Python library.

      Next Steps

      The app you’ve created shows off some of the aspects of the Linode API Python library. You can use LinodeLoginClient to authorize your OAuth app with the appropriate scopes, and can create Linodes through the use of LinodeClient.

      In extending this app, you might want to add multiple functionalities, like creating NodeBalancers from a list of available Linodes, or managing domains. To achieve this goal you’ll probably want to separate the login logic from the Linode creation logic. One way to do this would be store the OAuth token in the session, implementing a time-based expiration mechanism to expire your tokens instead.

      More Information

      You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

      Find answers, ask questions, and help others.

      This guide is published under a CC BY-ND 4.0 license.



      Source link

      How To Display Data from the DigitalOcean API with React


      The author selected the Wikimedia Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      Over the last few years, open-source web frameworks have greatly simplified the process of coding an application. React, for example, has only added to the popularity of JavaScript by making the language more accessible to new developers and increasing the productivity of seasoned developers. Created by Facebook, React allows developers to quickly create high-end user interfaces for highly-scalable web-applications by supporting such features as declarative views, state management, and client-side rendering, each of which can greatly reduce the complexity of building an app in JavaScript.

      You can leverage frameworks like React to load and display data from the DigitalOcean API, through which you can manage your Droplets and other products within the DigitalOcean cloud using HTTP requests. Although one can fetch data from an API with many other JavaScript frameworks, React provides useful benefits like lifecycles and local state management that make it particularly well-suited for the job. With React, the data retrieved from the API is added to the local state when the application starts and can go through various lifecycles as components mount and dismount. At any point, you can retrieve the data from your local state and display it accordingly.

      In this tutorial, you will create a simple React application that interacts with the DigitalOcean API v2 to make calls and retrieve information about your Droplets. Your app will display a list containing your current Droplets and their details, like name, region, and technical specifications, and you will use the front-end framework Bootstrap to style your application.

      Once you have finished this tutorial, you will have a basic interface displaying a list of your DigitalOcean Droplets, styled to look like the following:

      The final version of your React Application

      Prerequisites

      Before you begin this guide, you’ll need a DigitalOcean account and at least one Droplet set up, in addition to the following:

      Step 1 — Creating a Basic React Application

      In this first step, you’ll create a basic React application using the Create React App package from npm. This package automatically installs and configures the essential dependencies needed to run React, like the module builder Webpack and the JavaScript compiler Babel. After installing, you’ll run the Create React App package using the package runner npx, which comes pre-installed with Node.js.

      To install Create React App and create the first version of your application, run the following command, replacing my-app with the name you want to give to your application:

      • npx create-react-app my-app

      After the installation is complete, move into the new project directory and start running the application using these commands:

      The preceding command starts a local development server provided by Create React App, which disables the command line prompt in your terminal. To proceed with the tutorial, open up a new terminal window and navigate back to the project directory before proceeding to the next step.

      You now have the first version of your React application running in development mode, which you can view by opening http://localhost:3000 in a web browser. At this point, your app will only display the welcome screen from Create React App:

      The first version of your React application

      Now that you have installed and created the first version of your React application, you can add a table component to your app that will eventually hold the data from the DigitalOcean API.

      Step 2 — Creating a Component to Show the Droplet Data

      In this step, you will create the first component that displays information about your Droplets. This component will be a table that lists all of your Droplets and their corresponding details.

      The DigitalOcean API documentation states that you can retrieve a list containing all of your Droplets by sending a request to the following endpoint using cURL: https://api.digitalocean.com/v2/droplets. Using the output from this request, you can create a table component containing id, name, region, memory, vcpus, and disk for each Droplet. Later on in this tutorial, you'll insert the data retrieved from the API into the table component.

      To define a clear structure for your application, create a new directory called components inside the src directory where you'll store all the code you write. Create a new file called Table.js inside the src/components directory and open it with nano or a text editor of your choice:

      • mkdir src/components
      • nano src/components/Table.js

      Define the table component by adding the following code to the file:

      src/components/Table.js

      import React from 'react';
      
      const Table = () => {
        return (
          <table>
            <thead>
              <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Region</th>
                <th>Memory</th>
                <th>CPUs</th>
                <th>Disk Size</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
              </tr>
            </tbody>
          </table>
        );
      }
      
      export default Table
      

      The code block above imports the React framework and defines a new component called Table, which consists of a table with a heading and a body.

      When you have added these lines of code, save and exit the file. With the nano text editor, you can do this by pressing CTRL+X, typing y, and pressing ENTER.

      Now that you have created the table component, it is time to include this component in your application. You'll do this by importing the component into the entry point of the application, which is in the file src/App.js. Open this file with the following command:

      Next, remove the boilerplate code that displays the Create React App welcome message in src/App.js, which is highlighted in the following code block.

      src/App.js

      import React, { Component } from 'react';
      import logo from './logo.svg';
      import './App.css';
      
      class App extends Component {
        render() {
          return (
            <div className="App">
              <header className="App-header">
                <img src={logo} className="App-logo" alt="logo" />
                <p>
                  Edit <code>src/App.js</code> and save to reload.
                </p>
                <a
                  className="App-link"
                  href="https://reactjs.org"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  Learn React
                </a>
              </header>
            </div>
          );
        }
      }
      
      export default App;
      

      After removing the lines that displayed the welcome message, include the table component inside this same file by adding the following highlighted lines:

      src/App.js

      import React, { Component } from 'react';
      import Table from './components/Table.js';
      
      class App extends Component {
        render() {
          return (
            <div className="App">
              <Table />
            </div>
          );
        }
      }
      
      export default App;
      

      If you access http://localhost:3000 in your web browser again, your application will now display a basic table with table heads:

      The React application with a basic table

      In this step, you have created a table component and included this component into the entry point of your application. Next, you will set up a connection to the DigitalOcean API, which you'll use to retrieve the data that this table will display.

      Step 3 — Securing Your API Credentials

      Setting up a connection to the DigitalOcean API consists of several actions, starting with safely storing your Personal Access Token as an environment variable. This can be done by using dotenv, a package that allows you to store sensitive information in a .env file that your application can later access from the environment.

      Use npm to install the dotenv package:

      After installing dotenv, create an environment file called .env in the root directory of your application by executing this command:

      Add the following into .env, which contains your Personal Access Token and the URL for the DigitalOcean API :

      .env

      DO_API_URL=https://api.digitalocean.com/v2
      DO_ACCESS_TOKEN=YOUR_API_KEY
      

      To ensure this sensitive data doesn't get committed to a repository, add it to your .gitignore file with the following command:

      • echo ".env" >> .gitignore

      You have now created a safe and simple configuration file for your environment variables, which will provide your application with the information it needs to send requests to the DigitalOcean API. To ensure your API credentials aren't visible on the client side, you will next set up a proxy server to forward requests and responses between your application server and the DigitalOcean API.

      Install the middleware http-proxy-middleware by executing the following command:

      • npm install http-proxy-middleware

      After installing this, the next step is to set up your proxy. Create the setupProxy.js file in the src directory:

      Inside this file, add the following code to set up the proxy server:

      src/setupProxy.js

      const proxy = require('http-proxy-middleware')
      
      module.exports = function(app) {
      
        require('dotenv').config()
      
        const apiUrl = process.env.DO_API_URL
        const apiToken = process.env.DO_ACCESS_TOKEN
        const headers  = {
          "Content-Type": "application/json",
          "Authorization": "Bearer " + apiToken
        }
      
        // define http-proxy-middleware
        let DOProxy = proxy({
          target: apiUrl,
          changeOrigin: true,
        pathRewrite: {
          '^/api/' : '/'
        },
          headers: headers,
        })
      
        // define the route and map the proxy
        app.use('/api', DOProxy)
      
      };
      

      In the preceding code block, const apiURL = sets the url for the DigitalOcean API as the endpoint, and const apiToken = loads your Personal Access Token into the proxy server. The option pathRewrite mounts the proxy server to /api rather than / so that it does not interfere with the application server but still matches the DigitalOcean API.

      You've now successfully created a proxy server that will send all API requests made from your React application to the DigitalOcean API. This proxy server will make sure your Personal Access Token, which is safely stored as an environment variable, isn't exposed on the client side. Next, you will create the actual requests to retrieve your Droplet data for your application.

      Step 4 — Making API Calls to DigitalOcean

      Now that your display component is ready and the connection details to DigitalOcean are stored and secured through a proxy server, you can start retrieving data from the DigitalOcean API. First, add the following highlighted lines of code to src/App.js just before and after you declare the class App:

      src/App.js

      import React, { Component } from 'react';
      ...
      class App extends Component {
        constructor(props) {
          super(props);
          this.state = {
            droplets: []
          }
        }
      
          render() {
      ...
      

      These lines of code call a constructor method in your class component, which in React initializes the local state by providing this.state with an object or objects. In this case, the objects are your Droplets. From the code block above, you can see that the array containing your Droplets is empty, making it possible to fill it with the results from the API call.

      In order to display your current Droplets, you'll need to fetch this information from the DigitalOcean API. Using the JavaScript function Fetch, you'll send a request to the DigitalOcean API and update the state for droplets with the data you retrieve. You can do this using the componentDidMount method by adding the following lines of code after the constructor:

      src/App.js

      class App extends Component {
        constructor(props) {
          super(props);
          this.state = {
            droplets: []
          }
        }
      
        componentDidMount() {
          fetch('http://localhost:3000/api/droplets')
          .then(res => res.json())
          .then(json => json.droplets)
          .then(droplets => this.setState({ 'droplets': droplets }))
        }
      ...
      

      With your Droplet data stored into the state, it's time to retrieve it within the render function of your application and to send this data as a prop to the table component. Add the following highlighted statement to the table component in App.js:

      src/App.js

      ...
      class App extends Component {
        render() {
          return (
            <div className="App">
              <Table droplets={ this.state.droplets } />
            </div>
          );
        }
      }
      ...
      

      You have now created the functionality to retrieve data from the API, but you still need to make this data accessible via a web browser. In the next step, you will accomplish this by displaying your Droplet data in your table component.

      Step 5 — Displaying Droplet Data in Your Table Component

      Now that you have transferred the Droplet data to the table component, you can iterate this data over rows in the table. But since the application makes the request to the API after App.js is mounted, the property value for droplets will be empty at first. Therefore, you also need to add code to make sure droplets isn't empty before you try to display the data. To do this, add the following highlighted lines to the tbody section of Table.js:

      src/components/Table.js

      const Table = ({ droplets }) => {
        return (
          <table>
            <thead>
              <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Region</th>
                <th>Memory</th>
                <th>CPUs</th>
                <th>Disk Size</th>
              </tr>
            </thead>
            <tbody>
              { (droplets.length > 0) ? droplets.map( (droplet, index) => {
                 return (
                  <tr key={ index }>
                    <td>{ droplet.id }</td>
                    <td>{ droplet.name }</td>
                    <td>{ droplet.region.slug}</td>
                    <td>{ droplet.memory }</td>
                    <td>{ droplet.vcpus }</td>
                    <td>{ droplet.disk }</td>
                  </tr>
                )
               }) : <tr><td colSpan="5">Loading...</td></tr> }
            </tbody>
          </table>
        );
      }
      

      With the addition of the preceding code, your application will display a Loading... placeholder message when no Droplet data is present. When the DigitalOcean API does return Droplet data, your application will iterate it over table rows containing columns for each data type and will display the result to your web browser:

      The React Application with Droplet data

      Note: If your web browser displays an error at http://localhost:3000, press CTRL+C in the terminal that is running your development server to stop your application. Run the following command to restart your application:

      In this step, you have modified the table component of your application to display your Droplet data in a web browser and added a placeholder message for when there are no Droplets found. Next, you will use a front-end web framework to style your data to make it more visually appealing and easier to read.

      Step 6 — Styling Your Table Component Using Bootstrap

      Your table is now populated with data, but the information is not displayed in the most appealing manner. To fix this, you can style your application by adding Bootstrap to your project. Bootstrap is an open-source styling and component library that lets you add responsive styling to a project with CSS templates.

      Install Bootstrap with npm using the following command:

      After Bootstrap has finished installing, import its CSS file into your project by adding the following highlighted line to src/App.js:

      src/App.js

      import React, { Component } from 'react';
      import Table from './components/Table.js';
      import 'bootstrap/dist/css/bootstrap.min.css';
      
      class App extends Component {
      ...
      

      Now that you have imported the CSS, apply the Bootstrap styling to your table component by adding the class table to the <table> tag in src/components/Table.js.

      src/components/Table.js

      import React from 'react';
      
      const Table = ({ droplets }) => {
        return (
          <table className="table">
            <thead>
      ...
      

      Next, finish styling your application by placing a header above your table with a title and the DigitalOcean logo. Click on Download Logos in the Brand Assets section of DigitalOcean's Press page to download a set of logos, pick your favorite from the SVG directory (this tutorial uses DO_Logo_icon_blue.svg), and add it to your project by copying the logo file into a new directory called assets within the src directory of your project. After uploading the logo, import it into the header by adding the highlighted lines to src/App.js:

      src/App.js

      import React, { Component } from 'react';
      import Table from './components/Table.js';
      import 'bootstrap/dist/css/bootstrap.min.css';
      import logo from './assets/DO_Logo_icon_blue.svg';
      
      class App extends Component {
      ...
        render() {
          return (
            <div className="App">
              <nav class="navbar navbar-light bg-light">
                <a class="navbar-brand" href="./">
                  <img src={logo} alt="logo" width="40" /> My Droplets
                </a>
              </nav>
              <Table droplets={ this.state.droplets } />
            </div>
          );
        }
      }
      
      export default App;
      

      In the preceding code block, the classes within the nav tag add a particular styling from Bootstrap to your header.

      Now that you have imported Bootstrap and applied its styling to your application, your data will show up in your web browser with an organized and legible display:

      The final version of your React Application

      Conclusion

      In this article, you've created a basic React application that fetches data from the DigitalOcean API through a secured proxy server and displays it with Bootstrap styling. Now that you are familiar with the React framework, you can apply the concepts you learned here to more complicated applications, such as the one found in How To Build a Modern Web Application to Manage Customer Information with Django and React on Ubuntu 18.04. If you want to find out what other actions are possible with the DigitalOcean API, have a look at the API documentation on DigitalOcean's website.



      Source link

      Como criar um Space e uma API Key na DigitalOcean


      Introdução

      DigitalOcean Spaces é um serviço de armazenamento de objetos que torna mais fácil e econômico armazenar e fornecer grandes quantidades de dados. Spaces individuais podem ser criados e colocados em uso rapidamete, sem necessidade de configuração adicional.

      Neste tutorial, iremos utilizar o Painel de Controle da DigitalOcean para criar um novo Space. Em seguida, iremos recuperar uma Chave de API ou API Key e um secret que podem ser utilizados para conceder acesso ao Space para quaisquer clientes ou bibliotecas compatíveis com S3.

      Pré-requisitos

      Para completar este tutorial, você vai precisar de uma conta na DigitalOcean. Se você já não tiver uma, você pode registrar uma nova na página de inscrição.

      Faça o login no Painel de Controle da DigitalOcean para começar.

      Criando um Space

      Para criar um novo Space, utilize o botão Create no canto superior direito do Painel de Controle. Clique no botão, em seguida escolha Spaces na lista suspensa:

      Se você nunca criou um Space antes, você também pode criar um diretamente da página do Spaces. Para fazer isso, clique Spaces na navegação principal do Painel de Controle, e então clique em Create a space. Qualquer uma das opções o levarão à tela Create a Space:

      Primeiro, escolha um nome para o seu Space. Esse nome deve ser único entre todos os Spaces (ou seja, nenhum outro usuário do Spaces pode ter o mesmo nome em qualquer região), deve ter de 3 a 63 caracteres, e pode conter apenas letras minúsculas, números e traços.

      Em seguida, escolha a região do datacenter onde você gostaria que seu Space estivesse. No momento em que esta captura de tela foi feita, nyc3 e ams3 eram as escolhas possíveis. Outras se tornarão dsponíveis ao longo do tempo.

      Finalmente, escolha se deseja que os usuários não autenticados possam listar todos os arquivos em seu Space. Isso não afeta o acesso a arquivos individuais (que é definido em uma base por aquivo), mas apenas a capacidade de obter uma lista de todos os arquivos. A escolha padrão de Private é segura, a menos que você tenha alguns scripts ou clientes que precisem buscar listagens de arquivos sem uma chave de acesso ou access key.

      Quando seu nome e as opções estiverem todos definidos, desça e clique no botão Create a Space. Seu Space será criado, e você será levado para a interface do navegador de arquivos:

      Se este é o seu primeiro Space, você terá um arquivo welcome.html, do contrário, o Space estará vazio.

      Tome nota da URL do seu Space. Está disponível logo abaixo do nome do Space na visualização do navegador de arquivos. Nesse caso de exemplo, a URL completa é https://example-name.nyc3.digitaloceanspaces.com. O nome do Space aqui (geralmente chamado de nome do bucket) é example-name. A URL do servidor (ou endereço) é a parte restante, consistindo do nome do datacenter seguido por .digitaloceanspaces.com: https://nyc3.digitaloceanspaces.com.

      Existem algumas maneiras diferentes pelas quais os clientes e bibliotecas solicitarão essas informações. Alguns vão querer no mesmo formato dado no Painel de Controle. Alguns exigem que o nome do bucket siga a URL do servidor, como em https://nyc3.digitaloceanspaces.com/example-name. Outros ainda pedirão para você inserir o endereço do servidor e o nome do bucket ou Space separadamente. Consulte a documentação do seu cliente ou biblioteca para mais orientações nesse item.

      A seguir, criaremos a chave que precisamos para acessar nossos Spaces a partir de clientes de terceiros.

      Criando uma Access Key

      Para acessar seus arquivos de fora do Painel de Controle da DigitalOcean, precisamos gerar uma chave de acesso ou access key e um secret. Estes são um par de tokens aleatórios que servem como nome de usuário e senha para conceder acesso ao seu Space.

      Primeiro, clique no link da API na navegação principal do Painel de Controle. A página resultante lista seus tokens de API da DigitalOcean e as chaves de acesso do Spaces. Role para baixo até a parte do Spaces:

      Se este é o seu primeiro Space, você não pode ter nenhuma chave listada. Clique no botão Generate New Key. A caixa de diálogo New Spaces key será exibida:

      Digite um nome para a chave. Você pode criar quantas chaves quiser, portanto, lembre-se de que a única maneira de revogar o acesso a uma chave é excluí-la. Desse modo, você pode querer particionar as chaves por pessoa, por equipe ou pelo software cliente no qual você as estiver utilizando.

      Neste caso, estamos criando uma chave chamada example-token. Clique no botão Generate Key para completar o processo. Você retornará à tela da API listando todas as suas chaves. Observe que a nova chave tem dois tokens longos exibidos:

      O primeiro é a sua access key. Isso não é secreto e continuará visível no Painel de Controle. A segunda string é o seu secret ou secret key. Isso só será exibido uma vez. Registre-a em um local seguro para uso posterior. Na próxima vez que você visitar a página da API, esse valor será eliminado e não há como recuperá-lo.

      Diferentes clientes compatíveis com S3 podem ter nomes sutilmente diferentes para access key e secret. A terminologia usada é normalmente próxima o suficiente para deixar claro qual token deve ir para onde. Caso contrário, consulte a documentação do seu cliente ou biblioteca para obter mais informações.

      Conclusão

      Neste tutorial criamos um novo Space na DigitalOcean e uma nova access key e secret. Agora sabemos nossa URL de servidor, nome do bucket (ou nome do Space), access key, e secret. Com essas informações você pode conectar praticamente qualquer cliente ou biblioteca compatível com S3 ao seu novo Space na DigitalOcean!

      Por Brian Boucheron



      Source link