mikekelly / hal-browser

An API browser for the hal+json media type
MIT License
835 stars 157 forks source link

How to include it in a react app #92

Open kopax opened 7 years ago

kopax commented 7 years ago

Hi mikekelly,

Thank you for your job. I am looking at your source code and try to port it into my app. I am experienced with backbone JS and jquery, but the way we use to build app with it is slitghly different and I struggle bringing all the logic together. Probably because of my unexperience with HAL and your app.

Is anyone interested working on a port to react ?

mikekelly commented 7 years ago

Yes, I'd be very interested in that. Also in structuring the code base in a way that makes it easier to work with the various packaging systems.

Unfortunately, I don't have the time to invest in this kind of a rewrite but I'd be happy to look to a port (perhaps as a 1.0 release)

kopax commented 7 years ago

I was working on a implementation in react but I found a short cut :

Extract all sources in one file :

#!/bin/bash

urlList=(
    'http://haltalk.herokuapp.com/explorer/vendor/js/jquery-1.10.2.min.js'
    'http://haltalk.herokuapp.com/explorer/vendor/js/jquery-1.10.2.min.js'
    'http://haltalk.herokuapp.com/explorer/vendor/js/underscore.js'
    'http://haltalk.herokuapp.com/explorer/vendor/js/backbone.js'
    'http://haltalk.herokuapp.com/explorer/vendor/js/uritemplates.js'
    'http://haltalk.herokuapp.com/explorer/vendor/js/URI.min.js'
    'http://haltalk.herokuapp.com/explorer/vendor/js/bootstrap.js'
    'http://haltalk.herokuapp.com/explorer/js/hal.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/browser.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/http/client.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/resource.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/browser.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/explorer.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/inspector.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/navigation.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/location_bar.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/request_headers.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/resource.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/properties.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/links.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/embedded_resources.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/embedded_resource.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/non_safe_request_dialog.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/query_uri_dialog.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/response.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/response_headers.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/response_body.js'
    'http://haltalk.herokuapp.com/explorer/js/hal/views/documentation.js'
)

count=0
while [ "x${urlList[count]}" != "x" ]
do
   count=$(( $count + 1 )) && curl -vs "${urlList[count]}"
done >halbrowser.js

Do the modification you need like adding the bearer token your self. Then upload the file somewhere like pastebin. If you want to use it for production don't forget to compress the javascript.

You can easely use it with a simple react-component like follow:

import React from 'react';
import Helmet from 'react-helmet';

class HalBrowserPage extends React.Component { 

  componentDidMount() {
    setTimeout(() => {
      const browser = new HAL.Browser({
        container: $('#browser'),
        token: JSON.parse(localStorage.token),
        entryPoint: '/'
      });
      Backbone.history.start();
    }, 1000);
  }

  render() {
    return (
      <div>
        <Helmet
          link={[
            { rel: 'stylesheet', href: 'http://haltalk.herokuapp.com/explorer/vendor/css/bootstrap.css' },
            { rel: 'stylesheet', href: 'http://haltalk.herokuapp.com/explorer/vendor/css/bootstrap-responsive.css' },
            { rel: 'stylesheet', href: 'http://haltalk.herokuapp.com/explorer/styles.css' },
          ]}
          script={[
            { src: 'http://pastebin.com/raw/iAiHWrRN', type: 'text/javascript' },
          ]}

        />
        <div id="browser"></div>
      </div>
    );
  }
}

You will still miss the script text/template from browser.html.

For the moment I have just added them in my index.html of my react app: (They are not jsx friendly)

index.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="manifest" href="manifest.json">
    <meta name="mobile-web-app-capable" content="yes">
    <title>testing</title>
  </head>
  <body>
    <noscript>If you're seeing this message, that means <strong>JavaScript has been disabled on your browser</strong>, please <strong>enable JS</strong> to make this app work.</noscript>
    <div id="app"></div>
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700" rel="stylesheet">

    <script id="location-bar-template" type="text/template">
      <form>
        <div class="input-append span12 location-bar-container">
          <input class="span11" id="appendedInputButton" type="text" value="<%= _.escape(url) %>">
          <button class="btn" type="submit">Go!</button>
          <span class="ajax-loader"></span>
        </div>
      </form>
    </script>

    <script id="links-template" type="text/template">
      <h2>Links</h2>
      <table class="table">
        <thead>
        <tr>
          <th>rel</th>
          <th>title</th>
          <th>name / index</th>
          <th>docs</th>
          <th>GET</th>
          <th>NON-GET</th>
        </tr>
        </thead>
        <tbody>
        <% _.each(links, function(obj, rel) { %>
        <% if ($.isArray(obj)) { %>
        <% _.each(obj, function(link, i) { %>
        <tr>
          <td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
          <td><%= link.title || '' %></td>
          <td><%= link.name ? 'name: ' + link.name : 'index: ' + i %></a></td>
          <td>
            <% if (HAL.isUrl(rel)) { %>
            <a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
            <% } %>
          </td>
          <td>
            <% if (link.templated === true) { %>
            <a class="query btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Query URI template"><i class="icon-question-sign"></i></a>
            <% } else { %>
            <a class="follow btn btn-success" href="<%= HAL.normalizeUrl(link.href) %>" title="Follow link"><i class="icon-arrow-right"></i></a>
            <% } %>
          </td>
          <td>
            <a class="non-get btn btn-warning" href="<%= HAL.normalizeUrl(link.href) %>" title="Perform non-GET request">!</a>
          </td>
        </tr>
        <% }); %>
        <% } else { %>
        <tr>
          <td><strong><%= HAL.truncateIfUrl(rel) %></strong></td>
          <td><%= obj.title || '' %></td>
          <td><%= obj.name || '' %></td>
          <td>
            <% if (HAL.isUrl(rel)) { %>
            <a class="dox" href="<%= HAL.normalizeUrl(HAL.buildUrl(rel)) %>"><i class="icon-book"></i></a>
            <% } %>
          </td>
          <td>
            <% if (obj.templated === true) { %>
            <a class="query btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Query URI template"><i class="icon-question-sign"></i></a>
            <% } else { %>
            <a class="follow btn btn-success" href="<%= HAL.normalizeUrl(obj.href) %>" title="Follow link"><i class="icon-arrow-right"></i></a>
            <% } %>
          </td>
          <td>
            <a class="non-get btn btn-warning" href="<%= HAL.normalizeUrl(obj.href) %>" title="Perform non-GET request">!</a>
          </td>
        </tr>
        <% } %>
        <% }) %>
        </tbody>
      </table>
    </script>

    <script id="properties-template" type="text/template">
      <h2>Properties</h2>
      <pre><%= properties %></pre>
    </script>

    <script id="request-headers-template" type="text/template">
      <h2>Custom Request Headers</h2>
      <textarea class="span12"></textarea>
    </script>

    <script id="response-headers-template" type="text/template">
      <h2>Response Headers</h2>
      <pre><%= status.code %> <%= status.text %>

<% _.each(headers, function(value, name) {
      %><%= _.escape(name) %>: <%
           if(HAL.isFollowableHeader(name)) {
           %><a href="<%= HAL.normalizeUrl(value) %>" class="follow"><%
           }
      %><%= _.escape(value)
      %><% if(HAL.isFollowableHeader(name)) {
           %></a><%
           } %>
<% }) %></pre>
    </script>

    <script id="response-body-template" type="text/template">
      <h2>Response Body</h2>
      <pre><%= _.escape(body) %></pre>
    </script>

    <script id="query-uri-template" type="text/template">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
        <h3>Expand URI Template</h3>
      </div>

      <form id="query" action="<%= href %>">
        <div class="modal-body">
          <p>URI Template:</p>
          <pre><%- href %></pre>
          <p>Input (JSON):</p>
          <textarea><%= input %></textarea>
          <p>Expanded URI:</p>
          <pre class="preview">&nbsp;</pre>
        </div>

        <div class="modal-footer">
          <button type="submit" class="btn btn-primary">Follow URI</button>
        </div>
      </form>
    </script>

    <script id="non-safe-request-template" type="text/template">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
        <h3>Make a non-GET request</h3>
      </div>

      <form class="non-safe" action="<%= href %>">
        <div class="modal-body">
          <p>Target URI</p>
          <input name="url" type="text" class="url" value="<%= href %>" />
          <p>Method:</p>
          <input name="method" type="text" class="method" value="POST" />
          <p>Headers:</p>
<textarea name="headers" class="headers" style="height: 100px">
Content-Type: application/json
<%= user_defined_headers %>
</textarea>
          <p>Body:</p>
<textarea name="body" class="body" style="height: 200px">
{

}
</textarea>
        </div>

        <div class="modal-footer">
          <button type="submit" class="btn btn-primary">Make Request</button>
        </div>
      </form>
    </script>

    <script id="embedded-resources-template" type="text/template">
      <h2>Embedded Resources</h2>
    </script>

    <script id="embedded-resource-template" type="text/template">
      <div class="accordion-heading">
        <a class="accordion-toggle" href="#"><%= resource.identifier %><% if (resource.title) { %>: <span class="embedded-resource-title"><%- resource.title %></span><% } %>
          <% if (HAL.isUrl(resource.embed_rel)) { %>
        <span class="dox pull-right" data-href="<%= HAL.normalizeUrl(HAL.buildUrl(resource.embed_rel)) %>">
          <i class="icon-book"></i>
        </span>
          <% } %>
        </a>
      </div>
    </script>
  </body>
</html>

I use it with react-router so the source get injected with react-helmet only when this page is displayed, and remove when you need it.

The whole code ported into react would have take much longer.

I have tried to edit the HAL.Client.prototype.get method to add my custom headers but it didn't work.

Do you know where I should modify the code in order to write this ?

     const browser = new HAL.Browser({
        container: $('#browser'),
        token: JSON.parse(localStorage.token)
        entryPoint: '/'
      });

One downside with this method.

The properties template will render [object Object] instead of the real object. I have checked, the object is empty)