Skip to content

Commit

Permalink
Add support for bulk user exports
Browse files Browse the repository at this point in the history
This commit adds the ability to create bulk user exports.  With this
update, a new request type of `StatusCodeRequest` was added in order to
handle queries against the `job_error` endpoint which report the error
result of a given job.

Please note:  With this change, consumers of the API will be responsible
for fetching and decompressing the `gzip` file produced from the job
request via the URL provided in the response.
  • Loading branch information
Manny Batule committed Sep 19, 2018
1 parent 772daf1 commit 096c30e
Show file tree
Hide file tree
Showing 14 changed files with 722 additions and 13 deletions.
117 changes: 107 additions & 10 deletions src/main/java/com/auth0/client/mgmt/JobsEntity.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.auth0.client.mgmt;

import com.auth0.json.mgmt.jobs.Job;
import com.auth0.json.mgmt.jobs.UsersExport;
import com.auth0.net.CustomRequest;
import com.auth0.net.Request;
import com.auth0.net.StatusCodeRequest;
import com.auth0.utils.Asserts;
import com.fasterxml.jackson.core.type.TypeReference;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
Expand All @@ -23,7 +26,7 @@ public class JobsEntity extends BaseManagementEntity {

/**
* Sends an Email Verification. A token with scope update:users is needed.
* See https://auth0.com/docs/api/management/v2#!/Jobs/post_verification_email
* See <a href=https://auth0.com/docs/api/management/v2#!/Jobs/post_verification_email>Post Verification Email</a>
*
* @param userId The user_id of the user to whom the email will be sent.
* @param clientId The id of the client, if not provided the global one will be used.
Expand All @@ -32,22 +35,116 @@ public class JobsEntity extends BaseManagementEntity {
public Request<Job> sendVerificationEmail(String userId, String clientId) {
Asserts.assertNotNull(userId, "user id");

String url = baseUrl
.newBuilder()
.addPathSegments("api/v2/jobs/verification-email")
.build()
.toString();
final String url = this.generateUrl("api/v2/jobs/verification-email");

Map<String, String> requestBody = new HashMap<>();
final Map<String, Object> requestBody = new HashMap<>();
requestBody.put("user_id", userId);
if (clientId != null && !clientId.isEmpty()) {
if (this.isValued(clientId)) {
requestBody.put("client_id", clientId);
}

CustomRequest<Job> request = new CustomRequest<>(client, url, "POST", new TypeReference<Job>() {
});
return this.generatePostRequest(url, requestBody);
}

/**
* Generates a job request to produce a long-running user export job.
* @see <a href="https://auth0.com/docs/api/management/v2#!/Jobs/post_users_exports">Post Users Exports</a>
*
* @return A request to execute the job.
*/
public Request<Job> exportUsers() { return this.exportUsers(new UsersExport()); }

/**
* Generates a job request to produce a long-running user export job.
* @see <a href="https://auth0.com/docs/api/management/v2#!/Jobs/post_users_exports">Post Users Exports</a>
*
* @param usersExport Object containing the parameters by which the user export job will be run.
* @return A request to execute the job.
*/
public Request<Job> exportUsers(final UsersExport usersExport) {
Asserts.assertNotNull(usersExport, "usersExport");

final String url = this.generateUrl("api/v2/jobs/users-exports");

final Map<String, Object> requestBody = new HashMap<>();
final String connectionId = usersExport.getConnectionId();
final String format = usersExport.getFormat();
final int limit = usersExport.getLimit();
final List<List<Map<String, String>>> fields = usersExport.getFields();

if (this.isValued(connectionId)) {
requestBody.put("connection_id", connectionId);
}

if (this.isValued(format)) {
requestBody.put("format", format);
}

if (limit != 0) {
requestBody.put("limit", Integer.toString(limit));
}

if ((fields != null) && (fields.size() > 0)) {
requestBody.put("fields", fields);
}

return this.generatePostRequest(url, requestBody);
}

/**
* Generates a request to get a job. Useful to check its status.
* @see <a href="https://auth0.com/docs/api/management/v2/#!/Jobs/get_results">Get a Job</a>
*
* @param jobId Id of the job to be retrieved
* @return A request to execute the job details retrieval.
*/
public Request<Job> getJob(final String jobId) {
Asserts.assertNotNull(jobId, "jobId");
final String url = this.generateUrl(String.format("api/v2/jobs/%s", jobId));
return this.generateJobRequest(url);
}

/**
* Generates a request to get the details of failed a job.
* @see <a href="https://auth0.com/docs/api/management/v2/#!/Jobs/get_errors">Get Job Error Details</a>
*
* @param jobId Id of the job error details to be retrieved
* @return A request to execute the job error details retrieval.
*/
public StatusCodeRequest getJobError(final String jobId) {
Asserts.assertNotNull(jobId, "jobId");
final String url = this.generateUrl(String.format("api/v2/jobs/%s/errors", jobId));
return this.generateJobErrorRequest(url);
}

private String generateUrl(final String restEndpoint) {
return baseUrl
.newBuilder()
.addPathSegments(restEndpoint)
.build()
.toString();
}

private boolean isValued(final String string) {
return (string != null) && (!string.isEmpty());
}

private CustomRequest<Job> generatePostRequest(final String url, final Map<String, Object> requestBody) {
final CustomRequest<Job> request = new CustomRequest<>(client, url, "POST", new TypeReference<Job>() {});
request.addHeader("Authorization", "Bearer " + apiToken);
request.setBody(requestBody);
return request;
}

private CustomRequest<Job> generateJobRequest(final String url) {
final CustomRequest<Job> request = new CustomRequest<>(client, url, "GET", new TypeReference<Job>() {});
request.addHeader("Authorization", "Bearer " + apiToken);
return request;
}

private StatusCodeRequest generateJobErrorRequest(final String url) {
final StatusCodeRequest request = new StatusCodeRequest(client, url);
request.addHeader("Authorization", "Bearer " + apiToken);
return request;
}
}
49 changes: 48 additions & 1 deletion src/main/java/com/auth0/json/mgmt/jobs/Job.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.auth0.json.mgmt.jobs;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Date;

Expand All @@ -21,6 +25,14 @@ public class Job {
private Date createdAt;
@JsonProperty("id")
private String id;
@JsonProperty("connection_id")
private String connectionId;
@JsonProperty("location")
private String location;
@JsonProperty("percentage_done")
private Integer percentageDone;
@JsonProperty("time_left_seconds")
private Integer timeLeftSeconds;

@JsonCreator
private Job(@JsonProperty("status") String status, @JsonProperty("type") String type, @JsonProperty("id") String id) {
Expand Down Expand Up @@ -49,4 +61,39 @@ public Date getCreatedAt() {
public String getId() {
return id;
}

@JsonProperty("connection_id")
public String getConnectionId() {
return connectionId;
}

@JsonProperty("location")
public String getLocation() {
return location;
}

@JsonProperty("location")
public void setLocation(final String location) {
this.location = location;
}

@JsonProperty("percentage_done")
public Integer getPercentageDone() {
return percentageDone;
}

@JsonProperty("percentage_done")
public void setPercentageDone(final Integer percentageDone) {
this.percentageDone = percentageDone;
}

@JsonProperty("time_left_seconds")
public Integer getTimeLeftSeconds() {
return timeLeftSeconds;
}

@JsonProperty("time_left_seconds")
public void setTimeLeftSeconds(final Integer timeLeftSeconds) {
this.timeLeftSeconds = timeLeftSeconds;
}
}
36 changes: 36 additions & 0 deletions src/main/java/com/auth0/json/mgmt/jobs/JobError.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.auth0.json.mgmt.jobs;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Class that represents an Auth0 Job Error object. Related to the {@link com.auth0.client.mgmt.JobsEntity} entity.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JobError {

@JsonProperty("statusCode")
private Integer statusCode;
@JsonProperty("message")
private String message;

@JsonCreator
public JobError(@JsonProperty("statusCode") Integer statusCode, @JsonProperty("message") String message) {
this.statusCode = statusCode;
this.message = message;
}

@JsonProperty("statusCode")
public Integer getStatusCode() {
return statusCode;
}

@JsonProperty("message")
public String getMessage() {
return message;
}
}
74 changes: 74 additions & 0 deletions src/main/java/com/auth0/json/mgmt/jobs/UsersExport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.auth0.json.mgmt.jobs;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;
import java.util.Map;

/**
* Class that represents an Auth0 Users Export object. Related to the {@link com.auth0.client.mgmt.JobsEntity} entity.
*/
@SuppressWarnings({"unused", "WeakerAccess"})
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class UsersExport {

@JsonProperty("connection_id")
private String connectionId;
@JsonProperty("format")
private String format;
@JsonProperty("limit")
private int limit;
@JsonProperty("fields")
private List<List<Map<String, String>>> fields = null;

@JsonCreator
public UsersExport() {}

@JsonCreator
public UsersExport(
@JsonProperty("connection_id") String connectionId,
@JsonProperty("format") String format,
@JsonProperty("limit") int limit,
@JsonProperty("fields") List<List<Map<String, String>>> fields) {
this.connectionId = connectionId;
this.format = format;
this.limit = limit;
this.fields = fields;
}

@JsonProperty("connection_id")
public String getConnectionId() { return connectionId; }

@JsonProperty("connection_id")
public void setConnectionId(final String connectionId) {
this.connectionId = connectionId;
}

@JsonProperty("format")
public String getFormat() { return format; }

@JsonProperty("format")
public void setFormat(final String format) {
this.format = format;
}

@JsonProperty("limit")
public int getLimit() { return limit; }

@JsonProperty("limit")
public void setFields(final List<List<Map<String, String>>> fields) {
this.fields = fields;
}

@JsonProperty("fields")
public List<List<Map<String, String>>> getFields() { return fields; }

@JsonProperty("fields")
public void setLimit(final int limit) {
this.limit = limit;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/auth0/net/StatusCodeRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.auth0.net;

import com.auth0.exception.APIException;
import com.auth0.exception.Auth0Exception;
import com.auth0.json.mgmt.jobs.JobError;
import com.fasterxml.jackson.core.type.TypeReference;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

import java.io.IOException;

public class StatusCodeRequest extends EmptyBodyRequest<JobError> {

public StatusCodeRequest(final OkHttpClient client, final String url) {
super(client, url, "GET", new TypeReference<JobError>() {});
}

@Override
protected RequestBody createBody() {
return null;
}

@Override
protected JobError parseResponse(final Response response) throws Auth0Exception {
if (!response.isSuccessful()) {
throw createResponseException(response);
}

try (final ResponseBody body = response.body()) {
final String payload = body.string();
return new JobError(response.code(), payload);
} catch (final IOException e) {
throw new APIException("Failed to parse response", response.code(), e);
}
}
}
3 changes: 3 additions & 0 deletions src/test/java/com/auth0/client/MockServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ public class MockServer {
public static final String MGMT_EMAIL_VERIFICATION_TICKET = "src/test/resources/mgmt/email_verification_ticket.json";
public static final String MGMT_EMPTY_LIST = "src/test/resources/mgmt/empty_list.json";
public static final String MGMT_JOB_POST_VERIFICATION_EMAIL = "src/test/resources/mgmt/post_verification_email.json";
public static final String MGMT_JOB_POST_USERS_EXPORT = "src/test/resources/mgmt/post_users_export.json";
public static final String MGMT_JOB_GET = "src/test/resources/mgmt/get_job.json";
public static final String MGMT_JOB_ERROR_GET = "src/test/resources/mgmt/get_job_error.json";


private final MockWebServer server;
Expand Down
Loading

0 comments on commit 096c30e

Please sign in to comment.