-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Grafana api #86
Grafana api #86
Changes from 5 commits
75cc1b6
bb1ab47
cca73d5
4782145
c4b3d57
fecebc7
0c00af5
6ab2a39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package datasources; | ||
package datasources.elasticsearch; | ||
|
||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package datasources; | ||
package datasources.elasticsearch; | ||
|
||
import lombok.Data; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package datasources.grafana; | ||
|
||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import models.Organization; | ||
import models.User; | ||
import org.apache.commons.lang3.RandomStringUtils; | ||
import org.jetbrains.annotations.NotNull; | ||
import play.Configuration; | ||
import play.Logger; | ||
import play.libs.Json; | ||
import play.libs.ws.WSClient; | ||
import play.libs.ws.WSRequest; | ||
import play.mvc.Http; | ||
|
||
import javax.inject.Inject; | ||
import javax.inject.Named; | ||
import java.security.SecureRandom; | ||
import java.util.UUID; | ||
import java.util.concurrent.CompletionStage; | ||
|
||
public class GrafanaClient { | ||
|
||
private final WSClient ws; | ||
private final String baseUrl; | ||
private final String apiKey; | ||
private final String adminUser; | ||
private final String adminPassword; | ||
|
||
@Inject | ||
public GrafanaClient(WSClient ws, @Named("grafana") Configuration grafanaConf) { | ||
this.ws = ws; | ||
|
||
this.apiKey = grafanaConf.getString("api_key"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So are we going to use the api_key at the end? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to, but there is something wrong in grafana that does not let me. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
this.adminUser = grafanaConf.getString("admin_user"); | ||
this.adminPassword = grafanaConf.getString("admin_password"); | ||
|
||
String scheme = grafanaConf.getString("scheme"); | ||
String host = grafanaConf.getString("host"); | ||
String port = grafanaConf.getString("port"); | ||
this.baseUrl = scheme + "://" + host + ":" + port; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Java has a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes but the Play client only accept String, I don't know if it's worth the double transformation, String -> URL -> String There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can pass from string to URL and URL to string. The only benefit you are going to have is validating the URL in construction, but it's not so important here :) |
||
} | ||
|
||
public CompletionStage<GrafanaResponse> createUser(final User user) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are you using any kind of checkstyle? do you have a typo here, a double space between public and return value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no checkstyle in this project I will add one later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. try to add as soon as possible because fix check style after having a lot of code could be a pain in the ass. @pedrovgs or I can help you with this one. |
||
String adminUserEndpoint = "/api/admin/users"; | ||
|
||
String grafanaPassword = generatePassword(); | ||
ObjectNode userRequest = Json.newObject() | ||
.put("name", user.getName()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. have sense move this request properties to constants? |
||
.put("email", user.getEmail()) | ||
.put("password", grafanaPassword); | ||
|
||
UUID userId = user.getId(); | ||
|
||
Logger.debug(userRequest.toString()); | ||
|
||
return getWsRequestForUserCreation(adminUserEndpoint).post(userRequest).thenApply(response -> { | ||
Logger.debug(response.getBody()); | ||
if (response.getStatus() == Http.Status.OK) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we need to do something if the request fails? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sound good move this ok to a method no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pedrovgs I created a ticket #88, for the MVP I don't want to handle the failure of grafana api. |
||
String id = response.asJson().get("id").toString(); | ||
User createdUser = User.find.byId(userId); | ||
createdUser.setGrafanaUserId(id); | ||
createdUser.setGrafanaPassword(grafanaPassword); | ||
createdUser.save(); | ||
} | ||
return Json.fromJson(response.asJson(), GrafanaResponse.class); | ||
}); | ||
} | ||
|
||
public CompletionStage<GrafanaResponse> addUserToOrganisation(User user, Organization organization) { | ||
String adminUserEndpoint = "/api/orgs/:orgId/users".replaceFirst(":orgId", organization.grafanaId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you move those urls to a constants? |
||
|
||
ObjectNode userRequest = Json.newObject() | ||
.put("loginOrEmail", user.getEmail()) | ||
.put("role", "Viewer"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you don't want to create json manually you can create a class as you tend to do with the responses :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I no strong opinion about that, coming from swift, this seems fine, until I need this model more than once. |
||
|
||
UUID userId = user.getId(); | ||
|
||
Logger.debug(userRequest.toString()); | ||
|
||
return getWsRequestForUserCreation(adminUserEndpoint).post(userRequest).thenApply(response -> { | ||
Logger.debug(response.getBody()); | ||
return Json.fromJson(response.asJson(), GrafanaResponse.class); | ||
}); | ||
} | ||
|
||
public CompletionStage<GrafanaResponse> deleteUserInDefaultOrganisation(User user) { | ||
String adminUserEndpoint = "/api/orgs/:orgId/users/:userId".replaceFirst(":orgId", "1").replaceFirst(":userId", user.getGrafanaUserId()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As this string is repeated in some methods we could extract it into a constant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
return getWsRequestForUserCreation(adminUserEndpoint).delete().thenApply(response -> { | ||
Logger.debug(response.getBody()); | ||
return Json.fromJson(response.asJson(), GrafanaResponse.class); | ||
}); | ||
} | ||
|
||
private WSRequest getWsRequestForUserCreation(String adminUserEndpoint) { | ||
WSRequest wsRequest = ws.url(baseUrl + adminUserEndpoint).setHeader("Accept", "application/json").setContentType("application/json") | ||
.setAuth(this.adminUser, this.adminPassword); | ||
Logger.debug(wsRequest.getHeaders().toString()); | ||
return wsRequest; | ||
} | ||
|
||
private WSRequest getWsRequest(String adminUserEndpoint) { | ||
WSRequest wsRequest = ws.url(baseUrl + adminUserEndpoint).setHeader("Accept", "application/json").setContentType("application/json") | ||
.setHeader("Authorization", "Bearer " + this.apiKey); | ||
Logger.debug(wsRequest.getHeaders().toString()); | ||
return wsRequest; | ||
} | ||
|
||
@NotNull | ||
private String generatePassword() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice password generator!! I'd move this method to a |
||
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()-_=+[{]}\\|;:\'\",<.>/?"; | ||
return RandomStringUtils.random(255, 0, 0, false, false, characters.toCharArray(), new SecureRandom()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package datasources.grafana; | ||
|
||
|
||
import lombok.Data; | ||
|
||
@Data | ||
public class GrafanaResponse { | ||
private String message; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,37 +4,76 @@ | |
import com.feth.play.module.pa.service.AbstractUserService; | ||
import com.feth.play.module.pa.user.AuthUser; | ||
import com.feth.play.module.pa.user.AuthUserIdentity; | ||
import datasources.grafana.GrafanaClient; | ||
import models.Organization; | ||
import models.User; | ||
import play.Logger; | ||
|
||
import javax.inject.Inject; | ||
import javax.inject.Singleton; | ||
import java.util.concurrent.ExecutionException; | ||
|
||
@Singleton | ||
public class FlowUpUserService extends AbstractUserService { | ||
|
||
private final GrafanaClient grafanaClient; | ||
|
||
@Inject | ||
public FlowUpUserService(final PlayAuthenticate auth) { | ||
public FlowUpUserService(PlayAuthenticate auth, GrafanaClient grafanaClient) { | ||
super(auth); | ||
this.grafanaClient = grafanaClient; | ||
} | ||
|
||
@Override | ||
public Object save(final AuthUser authUser) { | ||
public Object save(AuthUser authUser) { | ||
final boolean isLinked = User.existsByAuthUserIdentity(authUser); | ||
if (!isLinked) { | ||
return User.create(authUser).id; | ||
User user = User.create(authUser); | ||
createGrafanaUser(user); | ||
addUserToGrafanaOrg(user); | ||
return user.getId(); | ||
} else { | ||
User user = User.findByAuthUserIdentity(authUser); | ||
createGrafanaUser(user); | ||
addUserToGrafanaOrg(user); | ||
// we have this user already, so return null | ||
return null; | ||
} | ||
} | ||
|
||
private void createGrafanaUser(User user) { | ||
try { | ||
grafanaClient.createUser(user).toCompletableFuture().get(); | ||
} catch (InterruptedException e) { | ||
Logger.debug(e.getMessage()); | ||
} catch (ExecutionException e) { | ||
Logger.debug(e.getMessage()); | ||
} | ||
} | ||
|
||
private void addUserToGrafanaOrg(User user) { | ||
String[] split = user.getEmail().split("@"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If our user has a gmail email is going to share the org with other gmail users? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, domain should be set in Org configuration. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's for a future PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, but if we only allow domains that are not @gmail.com how is a regular user w//o working at any organization going to test the platform? |
||
if (split.length == 2) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you give semantic to this 2 with a constant? |
||
Organization organization = Organization.findByGoogleAccount("@" + split[1]); | ||
try { | ||
grafanaClient.addUserToOrganisation(user, organization).toCompletableFuture().get(); | ||
user.refresh(); | ||
grafanaClient.deleteUserInDefaultOrganisation(user).toCompletableFuture().get(); | ||
} catch (InterruptedException e) { | ||
Logger.debug(e.getMessage()); | ||
} catch (ExecutionException e) { | ||
Logger.debug(e.getMessage()); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public Object getLocalIdentity(final AuthUserIdentity identity) { | ||
// For production: Caching might be a good idea here... | ||
// ...and dont forget to sync the cache when users get deactivated/deleted | ||
final User u = User.findByAuthUserIdentity(identity); | ||
if(u != null) { | ||
return u.id; | ||
return u.getId(); | ||
} else { | ||
return null; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
have sense move this grafana literal to a constant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do it, it feels like a java thing to me :D
have sense