Skip to content

Commit

Permalink
Merge pull request #1654 from HubSpot/custom_links
Browse files Browse the repository at this point in the history
Ability to specify quick links for requests
  • Loading branch information
ssalinas committed Dec 5, 2017
2 parents 2e07520 + 9b19243 commit ce5b60b
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
Expand Down Expand Up @@ -106,6 +107,10 @@ public static RootUrlMode parse(String value) {
@JsonProperty
private Optional<String> staticRootOverride = Optional.absent();

// e.g. {"request":{"SERVICE":[{"title":"my link","template":"http://example.com/{{request.id}}"}]}}
@JsonProperty
private Map<String, Map<String, List<UIQuickLinkConfiguration>>> quickLinks = Collections.emptyMap();

public boolean isHideNewDeployButton() {
return hideNewDeployButton;
}
Expand Down Expand Up @@ -281,4 +286,12 @@ public Optional<String> getStaticRootOverride() {
public void setStaticRootOverride(Optional<String> staticRootOverride) {
this.staticRootOverride = staticRootOverride;
}

public Map<String, Map<String, List<UIQuickLinkConfiguration>>> getQuickLinks() {
return quickLinks;
}

public void setQuickLinks(Map<String, Map<String, List<UIQuickLinkConfiguration>>> quickLinks) {
this.quickLinks = quickLinks;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.hubspot.singularity.config;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties( ignoreUnknown = true )
public class UIQuickLinkConfiguration {
private String template;
private String title;

public String getTemplate() {
return template;
}

public void setTemplate(String template) {
this.template = template;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class IndexView extends View {
private final boolean generateAuthHeader;
private final String authCookieName;
private final String authTokenKey;
private final String quickLinks;

public IndexView(String singularityUriBase, String appRoot, IndexViewConfiguration configuration, ObjectMapper mapper) {
super("index.mustache");
Expand Down Expand Up @@ -130,6 +131,12 @@ public IndexView(String singularityUriBase, String appRoot, IndexViewConfigurati
this.generateAuthHeader = configuration.isGenerateAuthHeader();
this.authCookieName = uiConfiguration.getAuthCookieName();
this.authTokenKey = uiConfiguration.getAuthTokenKey();

try {
this.quickLinks = ow.writeValueAsString(uiConfiguration.getQuickLinks());
} catch (JsonProcessingException e) {
throw Throwables.propagate(e);
}
}

public String getAppRoot() {
Expand Down Expand Up @@ -264,6 +271,10 @@ public String getAuthTokenKey() {
return authTokenKey;
}

public String getQuickLinks() {
return quickLinks;
}

@Override
public String toString() {
return "IndexView{" +
Expand Down Expand Up @@ -300,6 +311,7 @@ public String toString() {
", generateAuthHeader=" + generateAuthHeader +
", authCookieName='" + authCookieName + '\'' +
", authTokenKey='" + authTokenKey + '\'' +
", quickLinks='" + quickLinks + '\'' +
"} " + super.toString();
}
}
3 changes: 2 additions & 1 deletion SingularityUI/app/assets/index.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
sentryDsn: "{{{sentryDsn}}}",
generateAuthHeader: {{{generateAuthHeader}}},
authCookieName: "{{{ authCookieName }}}",
authTokenKey: "{{{ authTokenKey }}}"
authTokenKey: "{{{ authTokenKey }}}",
quickLinks: {{{quickLinks}}}
};
</script>
<script src="{{{staticRoot}}}/js/vendor.bundle.js"></script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';

import { Button } from 'react-bootstrap';
import { Button, DropdownButton, MenuItem } from 'react-bootstrap';
import { Link } from 'react-router';

import JSONButton from '../../common/JSONButton';
Expand Down Expand Up @@ -172,6 +172,25 @@ const RequestActionButtons = ({requestParent, fetchRequest, fetchRequestHistory,
</RemoveButton>
);

const quickLinks = [];

Utils.maybe(config.quickLinks, ['request', request.requestType], []).forEach((link) => {
const maybeLink = Utils.template(link.template, requestParent);
if (maybeLink) {
quickLinks.push(
<MenuItem href={maybeLink}>{link.title}</MenuItem>
);
}
});
Utils.maybe(config.quickLinks, ['request', 'ALL'], []).forEach((link) => {
const maybeLink = Utils.template(link.template, requestParent);
if (maybeLink) {
quickLinks.push(
<MenuItem href={maybeLink}>{link.title}</MenuItem>
);
}
});

return (
<div>
<JSONButton linkClassName="btn btn-default" object={requestParent}>JSON</JSONButton>
Expand All @@ -184,6 +203,11 @@ const RequestActionButtons = ({requestParent, fetchRequest, fetchRequestHistory,
{maybeEditButton}
{maybeToggleHealthchecksButton}
{removeButton}
{quickLinks.length > 0 &&
<DropdownButton bsStyle="default" title="Quick Links" pullRight>
{quickLinks}
</DropdownButton>
}
</div>
);
};
Expand Down
29 changes: 28 additions & 1 deletion SingularityUI/app/utils.es6
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ const Utils = {
if (object.hasOwnProperty(path[0])) {
return Utils.maybe(
object[path[0]],
path.slice(1, path.length)
path.slice(1, path.length),
defaultValue
);
}

Expand Down Expand Up @@ -486,6 +487,32 @@ const Utils = {
}
const authToken = JSON.parse(authCookie)[config.authTokenKey];
return `Bearer ${ authToken }`;
},

template(template, data) {
const start = "{{";
const end = "}}";
const path = "[a-z0-9_$][\\.a-z0-9_]*";
const pattern = new RegExp(start + "\\s*("+ path +")\\s*" + end, "gi");
try {
return template.replace(pattern, (tag, token) => {
const tokenPath = token.split(".");
let value = data;
let i = 0;

for (; i < tokenPath.length; i++){
value = value[tokenPath[i]];
if (value == null){
throw tokenPath[i] + "' not found in " + tag;
}
if (i === tokenPath.length - 1){
return value;
}
}
});
} catch (err) {
return null;
}
}
};

Expand Down
3 changes: 2 additions & 1 deletion SingularityUI/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ var templateData = {
sentryDsn: process.env.SINGULARITY_SENTRY_DSN || '',
generateAuthHeader: process.env.SINGULARITY_GENERATE_AUTH_HEADER || 'false',
authTokenKey: process.env.SINGULARITY_AUTH_TOKEN_KEY || 'token',
authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || ''
authCookieName: process.env.SINGULARITY_AUTH_COOKIE_NAME || '',
quickLinks: process.env.SINGULARITY_QUICK_LINKS || '{}'
};

var dest = path.resolve(__dirname, 'dist');
Expand Down

0 comments on commit ce5b60b

Please sign in to comment.