Skip to content

Commit 4b41dc2

Browse files
authored
Merge pull request #1038 from GeorgianaElena/serve_ui_from_backend
Serve UI config from the backend
2 parents e1145f9 + f6a1540 commit 4b41dc2

File tree

7 files changed

+176
-59
lines changed

7 files changed

+176
-59
lines changed

binderhub/app.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@
3939
from .base import AboutHandler, Custom404, VersionHandler
4040
from .build import Build
4141
from .builder import BuildHandler
42+
from .config import ConfigHandler
4243
from .health import HealthHandler
4344
from .launcher import Launcher
4445
from .log import log_request
46+
from .repoproviders import RepoProvider
4547
from .registry import DockerRegistry
4648
from .main import MainHandler, ParameterizedMainHandler, LegacyRedirectHandler
4749
from .repoproviders import (GitHubRepoProvider, GitRepoProvider,
@@ -453,6 +455,18 @@ def _add_slash(self, proposal):
453455
List of Repo Providers to register and try
454456
"""
455457
)
458+
459+
@validate('repo_providers')
460+
def _validate_repo_providers(self, proposal):
461+
"""trait validator to ensure there is at least one repo provider"""
462+
if not proposal.value:
463+
raise TraitError("Please provide at least one repo provider")
464+
465+
if any([not issubclass(provider, RepoProvider) for provider in proposal.value.values()]):
466+
raise TraitError("Repository providers should inherit from 'binderhub.RepoProvider'")
467+
468+
return proposal.value
469+
456470
concurrent_build_limit = Integer(
457471
32,
458472
config=True,
@@ -728,6 +742,7 @@ def initialize(self, *args, **kwargs):
728742
{'path': os.path.join(self.tornado_settings['static_path'], 'images')}),
729743
(r'/about', AboutHandler),
730744
(r'/health', HealthHandler, {'hub_url': self.hub_url_local}),
745+
(r'/_config', ConfigHandler),
731746
(r'/', MainHandler),
732747
(r'.*', Custom404),
733748
]

binderhub/config.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from tornado.log import app_log
2+
from .base import BaseHandler
3+
4+
class ConfigHandler(BaseHandler):
5+
"""Serve config"""
6+
def generate_config(self):
7+
config = dict()
8+
for repo_provider_class_alias, repo_provider_class in self.settings["repo_providers"].items():
9+
config[repo_provider_class_alias] = repo_provider_class.labels
10+
return config
11+
12+
async def get(self):
13+
self.write(self.generate_config())

binderhub/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def get(self):
3535
google_analytics_code=self.settings['google_analytics_code'],
3636
google_analytics_domain=self.settings['google_analytics_domain'],
3737
extra_footer_scripts=self.settings['extra_footer_scripts'],
38+
repo_providers=self.settings['repo_providers'],
3839
)
3940

4041

binderhub/repoproviders.py

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,15 @@ class ZenodoProvider(RepoProvider):
212212
"""
213213
name = Unicode("Zenodo")
214214

215+
display_name = "Zenodo DOI"
216+
217+
labels = {
218+
"text": "Zenodo DOI (10.5281/zenodo.3242074)",
219+
"tag_text": "Git ref (branch, tag, or commit)",
220+
"ref_prop_disabled": True,
221+
"label_prop_disabled": True,
222+
}
223+
215224
async def get_resolved_ref(self):
216225
client = AsyncHTTPClient()
217226
req = HTTPRequest("https://doi.org/{}".format(self.spec),
@@ -249,8 +258,18 @@ class FigshareProvider(RepoProvider):
249258
Users must provide a spec consisting of the Figshare DOI.
250259
"""
251260
name = Unicode("Figshare")
261+
262+
display_name = "Figshare DOI"
263+
252264
url_regex = re.compile(r"(.*)/articles/([^/]+)/([^/]+)/(\d+)(/)?(\d+)?")
253265

266+
labels = {
267+
"text": "Figshare DOI (10.6084/m9.figshare.9782777.v1)",
268+
"tag_text": "Git ref (branch, tag, or commit)",
269+
"ref_prop_disabled": True,
270+
"label_prop_disabled": True,
271+
}
272+
254273
async def get_resolved_ref(self):
255274
client = AsyncHTTPClient()
256275
req = HTTPRequest("https://doi.org/{}".format(self.spec),
@@ -292,6 +311,15 @@ def get_build_slug(self):
292311
class DataverseProvider(RepoProvider):
293312
name = Unicode("Dataverse")
294313

314+
display_name = "Dataverse DOI"
315+
316+
labels = {
317+
"text": "Dataverse DOI (10.7910/DVN/TJCLKP)",
318+
"tag_text": "Git ref (branch, tag, or commit)",
319+
"ref_prop_disabled": True,
320+
"label_prop_disabled": True,
321+
}
322+
295323
async def get_resolved_ref(self):
296324
client = AsyncHTTPClient()
297325
req = HTTPRequest("https://doi.org/{}".format(self.spec),
@@ -349,8 +377,18 @@ class HydroshareProvider(RepoProvider):
349377
Users must provide a spec consisting of the Hydroshare resource id.
350378
"""
351379
name = Unicode("Hydroshare")
380+
381+
display_name = "Hydroshare resource"
382+
352383
url_regex = re.compile(r".*([0-9a-f]{32}).*")
353384

385+
labels = {
386+
"text": "Hydroshare resource id or URL",
387+
"tag_text": "Git ref (branch, tag, or commit)",
388+
"ref_prop_disabled": True,
389+
"label_prop_disabled": True,
390+
}
391+
354392
def _parse_resource_id(self, spec):
355393
match = self.url_regex.match(spec)
356394
if not match:
@@ -414,6 +452,15 @@ class GitRepoProvider(RepoProvider):
414452

415453
name = Unicode("Git")
416454

455+
display_name = "Git repository"
456+
457+
labels = {
458+
"text": "Arbitrary git repository URL (http://git.example.com/repo)",
459+
"tag_text": "Git ref (branch, tag, or commit)",
460+
"ref_prop_disabled": False,
461+
"label_prop_disabled": False,
462+
}
463+
417464
def __init__(self, *args, **kwargs):
418465
super().__init__(*args, **kwargs)
419466
self.url, unresolved_ref = self.spec.split('/', 1)
@@ -476,6 +523,8 @@ class GitLabRepoProvider(RepoProvider):
476523

477524
name = Unicode('GitLab')
478525

526+
display_name = "GitLab.com"
527+
479528
hostname = Unicode('gitlab.com', config=True,
480529
help="""The host of the GitLab instance
481530
@@ -525,6 +574,13 @@ def _default_git_credentials(self):
525574
return r'username=binderhub\npassword={token}'.format(token=self.private_token)
526575
return ""
527576

577+
labels = {
578+
"text": "GitLab.com repository or URL",
579+
"tag_text": "Git ref (branch, tag, or commit)",
580+
"ref_prop_disabled": False,
581+
"label_prop_disabled": False,
582+
}
583+
528584
def __init__(self, *args, **kwargs):
529585
super().__init__(*args, **kwargs)
530586
self.quoted_namespace, unresolved_ref = self.spec.split('/', 1)
@@ -584,6 +640,8 @@ class GitHubRepoProvider(RepoProvider):
584640
"""Repo provider for the GitHub service"""
585641
name = Unicode('GitHub')
586642

643+
display_name = 'GitHub'
644+
587645
# shared cache for resolved refs
588646
cache = Cache(1024)
589647

@@ -657,6 +715,13 @@ def _default_git_credentials(self):
657715
return r'username={token}\npassword=x-oauth-basic'.format(token=self.access_token)
658716
return ""
659717

718+
labels = {
719+
"text": "GitHub repository name or URL",
720+
"tag_text": "Git ref (branch, tag, or commit)",
721+
"ref_prop_disabled": False,
722+
"label_prop_disabled": False,
723+
}
724+
660725
def __init__(self, *args, **kwargs):
661726
super().__init__(*args, **kwargs)
662727
self.user, self.repo, self.unresolved_ref = tokenize_spec(self.spec)
@@ -823,15 +888,25 @@ class GistRepoProvider(GitHubRepoProvider):
823888
If master or no ref is specified the latest revision will be used.
824889
"""
825890

826-
name = Unicode('Gist')
827-
hostname = Unicode('gist.github.com')
891+
name = Unicode("Gist")
892+
893+
display_name = "Gist"
894+
895+
hostname = Unicode("gist.github.com")
828896

829897
allow_secret_gist = Bool(
830898
default_value=False,
831899
config=True,
832900
help="Flag for allowing usages of secret Gists. The default behavior is to disallow secret gists.",
833901
)
834902

903+
labels = {
904+
"text": "Gist ID (username/gistId) or URL",
905+
"tag_text": "Git commit SHA",
906+
"ref_prop_disabled": False,
907+
"label_prop_disabled": False,
908+
}
909+
835910
def __init__(self, *args, **kwargs):
836911
# We dont need to initialize entirely the same as github
837912
super(RepoProvider, self).__init__(*args, **kwargs)

binderhub/static/js/index.js

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {fit} from './vendor/xterm/addons/fit';
3131

3232
var BASE_URL = $('#base-url').data().url;
3333
var BADGE_BASE_URL = $('#badge-base-url').data().url;
34+
var config_dict = {};
3435

3536
function update_favicon(path) {
3637
var link = document.querySelector("link[rel*='icon']") || document.createElement('link');
@@ -59,50 +60,39 @@ function v2url(providerPrefix, repository, ref, path, pathType) {
5960
return url;
6061
}
6162

62-
function updateRepoText() {
63-
var text;
63+
function loadConfig(callback) {
64+
var req = new XMLHttpRequest();
65+
req.onreadystatechange = function() {
66+
if (req.readyState == 4 && req.status == 200)
67+
callback(req.responseText)
68+
};
69+
req.open('GET', BASE_URL + "_config", true);
70+
req.send(null);
71+
}
72+
73+
function setLabels() {
6474
var provider = $("#provider_prefix").val();
65-
var tag_text = "Git ref (branch, tag, or commit)";
75+
var text = config_dict[provider]["text"];
76+
var tag_text = config_dict[provider]["tag_text"];
77+
var ref_prop_disabled = config_dict[provider]["ref_prop_disabled"];
78+
var label_prop_disabled = config_dict[provider]["label_prop_disabled"];
6679
var placeholder = "HEAD";
67-
// first enable branch/ref field, some providers later disable it
68-
$("#ref").prop("disabled", false);
69-
$("label[for=ref]").prop("disabled", false);
70-
if (provider === "gh") {
71-
text = "GitHub repository name or URL";
72-
} else if (provider === "gl") {
73-
text = "GitLab.com repository or URL";
74-
}
75-
else if (provider === "gist") {
76-
text = "Gist ID (username/gistId) or URL";
77-
tag_text = "Git commit SHA";
78-
}
79-
else if (provider === "git") {
80-
text = "Arbitrary git repository URL (http://git.example.com/repo)";
81-
}
82-
else if (provider === "zenodo") {
83-
text = "Zenodo DOI (10.5281/zenodo.3242074)";
84-
$("#ref").prop("disabled", true);
85-
$("label[for=ref]").prop("disabled", true);
86-
}
87-
else if (provider === "figshare") {
88-
text = "Figshare DOI (10.6084/m9.figshare.9782777.v1)";
89-
$("#ref").prop("disabled", true);
90-
$("label[for=ref]").prop("disabled", true);
91-
}
92-
else if (provider === "hydroshare") {
93-
text = "Hydroshare resource id or URL";
94-
$("#ref").prop("disabled", true);
95-
$("label[for=ref]").prop("disabled", true);
96-
}
97-
else if (provider === "dataverse") {
98-
text = "Dataverse DOI (10.7910/DVN/TJCLKP)";
99-
$("#ref").prop("disabled", true);
100-
$("label[for=ref]").prop("disabled", true);
101-
}
80+
81+
$("#ref").attr('placeholder', placeholder).prop("disabled", ref_prop_disabled);
82+
$("label[for=ref]").text(tag_text).prop("disabled", label_prop_disabled);
10283
$("#repository").attr('placeholder', text);
10384
$("label[for=repository]").text(text);
104-
$("#ref").attr('placeholder', placeholder);
105-
$("label[for=ref]").text(tag_text);
85+
}
86+
87+
function updateRepoText() {
88+
if (Object.keys(config_dict).length === 0){
89+
loadConfig(function(res) {
90+
config_dict = JSON.parse(res);
91+
setLabels();
92+
});
93+
} else {
94+
setLabels();
95+
}
10696
}
10797

10898
function getBuildFormValues() {

binderhub/templates/index.html

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,44 +37,39 @@ <h4>New to Binder? Get started with a Zero-to-Binder tutorial in <a href="https:
3737
{% block form %}
3838
<form id="build-form" class="form jumbotron">
3939
<h4 id="form-header" class='row'>Build and launch a repository</h4>
40-
<input type="hidden" id="provider_prefix" value="gh"/>
40+
<input type="hidden" id="provider_prefix" value="{{repo_providers.keys() | list | first}}"/>
4141
<div class="form-group row">
42-
<label for="repository">Git repository URL (github.com, gitlab.com or self-host)</label>
42+
<label for="repository">{{(repo_providers.values() | list | first).labels.text}}</label>
4343
<div class="input-group">
4444
<div class="input-group-btn" id="url-type-btn">
4545
<button type="button" class="btn btn-secondary dropdown-toggle"
4646
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
4747
title="Specify source of repository"
4848
>
4949
<span id="provider_prefix-selected">
50-
GitHub
50+
{{(repo_providers.values() | list | first).display_name}}
5151
</span>
5252
<span class="caret"></span>
5353
</button>
5454
<ul class="dropdown-menu" id="provider_prefix_sel">
55-
<li class="dropdown-item" value="gh"><a href="#">GitHub</a></li>
56-
<li class="dropdown-item" value="gist"><a href="#">Gist</a></li>
57-
<li class="dropdown-item" value="gl"><a href="#">GitLab.com</a></li>
58-
<li class="dropdown-item" value="git"><a href="#">Git repository</a></li>
59-
<li class="dropdown-item" value="zenodo"><a href="#">Zenodo DOI</a></li>
60-
<li class="dropdown-item" value="figshare"><a href="#">Figshare DOI</a></li>
61-
<li class="dropdown-item" value="hydroshare"><a href="#">Hydroshare resource</a></li>
62-
<li class="dropdown-item" value="dataverse"><a href="#">Dataverse DOI</a></li>
55+
{% for provider_prefix, provider in repo_providers.items() %}
56+
<li class="dropdown-item" value={{provider_prefix}}><a href="#">{{provider.display_name}}</a></li>
57+
{% endfor %}
6358
</ul>
6459
</div>
65-
<input class="form-control" type="text" id="repository" data-lpignore="true" placeholder="GitHub repository name or link"/>
60+
<input class="form-control" type="text" id="repository" data-lpignore="true" placeholder="{{(repo_providers.values() | list | first).labels.text}}"/>
6661
</div>
6762
</div>
6863
<div class="form-row row">
6964
<div class="form-group col-md-4">
70-
<label for="ref">Git branch, tag, or commit</label>
71-
<input class="form-control" type="text" id="ref" placeholder="master"/>
65+
<label for="ref">{{(repo_providers.values() | list | first).labels.tag_text}}</label>
66+
<input class="form-control" type="text" id="ref" placeholder="HEAD"/>
7267
</div>
7368
<div class="form-group col-md-6">
74-
<label for="filepath"></label>
69+
<label for="filepath">Path to a notebook file (optional)</label>
7570
<div class="input-group">
7671
<input class="form-control" type="text" id="filepath"
77-
placeholder=""
72+
placeholder="Path to a notebook file (optional)"
7873
/>
7974
<div class="input-group-btn" id="url-or-file-btn">
8075
<button type="button" class="btn btn-secondary dropdown-toggle"
@@ -107,7 +102,8 @@ <h4 id="form-header" class='row'>Build and launch a repository</h4>
107102
<label>Copy the URL below and share your Binder with others:</label>
108103
</div>
109104
<div class="url-row">
110-
<pre id="basic-url-snippet" data-default="Fill in the fields to see a URL for sharing your Binder."></pre>
105+
<pre id="basic-url-snippet" data-default="Fill in the fields to see a URL for sharing your Binder."
106+
>Fill in the fields to see a URL for sharing your Binder.</pre>
111107
<img class="icon clipboard" src="{{static_url("images/copy-icon-black.svg")}}" data-clipboard-target="#basic-url-snippet" alt="Copy to clipboard">
112108
</div>
113109
</div>

0 commit comments

Comments
 (0)