Skip to content
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

Add support for bulk user exports #152

Closed
wants to merge 1 commit into from

Conversation

mfb2
Copy link

@mfb2 mfb2 commented Sep 19, 2018

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.

@mfb2
Copy link
Author

mfb2 commented Sep 19, 2018

This PR addresses issue #95 . Please let me know if you have questions, comments, concerns, or suggestions on this PR, I'm happy to assist.

Thank you!

@mfb2 mfb2 force-pushed the support_users_exports branch 3 times, most recently from f01153d to 096c30e Compare September 19, 2018 17:19
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);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a tough time mocking an appropriate response object that will force a throw on this line. This is causing CI coverage checks to fail due to insufficient coverage. Response and MockResponse are final classes, so they can't be sub-classed or modified to produce an IOException in order to fall into this block.

Thoughts?

Any suggestions on ways to proceed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping since class will disappear

@JsonProperty("limit")
private int limit;
@JsonProperty("fields")
private List<List<Map<String, String>>> fields = null;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize this structure is a bit yucky... This is due to the spec provided by Auth0's API docs; the sample JSON response has an array within an array, which in turn contains a collection of objects that represent the field information.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to move this into a new class. Lets create the UserExportField class with two string properties:

  • name (required, should be part of the constructor)
  • export_as (optional, available via setter).

Then this class (let's rename it to UserExportOptions) will have as fields: List<UserExportField> fields. I'm confident the double array schema you see in the API docs is wrong. But will need to try this manually once you make the changes to be sure.


import java.io.IOException;

public class StatusCodeRequest extends EmptyBodyRequest<JobError> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a new request type and corresponding JobError object in order to provide the ability to inspect the HTTP status code returned by the job_error API endpoint. There didn't appear to be any other way to get that info aside from manually mapping the HTTP status code into the field directly as part of the response parsing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The http status code is irrelevant. It will be a 200 plus the error description (if job failed) in the body of the response.
This class should be deleted and instead the caller method should return a Request<JobError>

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.
@mfb2 mfb2 force-pushed the support_users_exports branch from 096c30e to d859dfc Compare October 24, 2018 16:14
Copy link
Contributor

@lbalmaceda lbalmaceda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution and sorry for the delay. I've left you my initial assessment of the export functionality. I had to check with the backend team to ensure the API docs were correct. Anyway, I'll wait to hear back from you before reviewing in detail the tests.

*
* @return A request to execute the job.
*/
public Request<Job> exportUsers() { return this.exportUsers(new UsersExport()); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This signature is not needed. I can't imagine a scenario where I'd call the endpoint without any parameters

final int limit = usersExport.getLimit();
final List<List<Map<String, String>>> fields = usersExport.getFields();

if (this.isValued(connectionId)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the null-checks below this line should belong to the UserExport class. Any required properties should be set on instantiation and the constructor should validate them. Here you only need to check that the UserExport instance is not null like you're doing in the first line. Later when constructing the request, null values would be ignored by the function that inserts the values into the request.

return request;
}

private StatusCodeRequest generateJobErrorRequest(final String url) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called only once. I don't see the point on extracting it into a new method.

request.addHeader("Authorization", "Bearer " + apiToken);
request.setBody(requestBody);
return request;
}

private CustomRequest<Job> generateJobRequest(final String url) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called only once. I don't see the point on extracting it into a new method.

.toString();
}

private boolean isValued(final String string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this method and use Asserts.assertNotNull(propertyValue, "property description") instead. Any invalid value further than a null can be considered user error and will be ultimately validated by the server.

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping since class will disappear

final Map<String, Object> body = bodyFromRequest(recordedRequest);
assertThat(body.size(), is(4));
assertThat(body, hasEntry("connection_id", CONNECTION_ID));
assertThat(body, hasEntry("format", CSV));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should check that the fields and limit properties are also present with the correct values (Applies to all similar tests)

* @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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's name it requestUsersExport

* @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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally I'd say get is enough but since this class is handling many types of errors and requests, I think you made a good call here.

@JsonProperty("limit")
private int limit;
@JsonProperty("fields")
private List<List<Map<String, String>>> fields = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to move this into a new class. Lets create the UserExportField class with two string properties:

  • name (required, should be part of the constructor)
  • export_as (optional, available via setter).

Then this class (let's rename it to UserExportOptions) will have as fields: List<UserExportField> fields. I'm confident the double array schema you see in the API docs is wrong. But will need to try this manually once you make the changes to be sure.

@lbalmaceda
Copy link
Contributor

Hey @mfb2, are you still interested in this feature? Don't hesitate asking any follow up questions.

@mfb2
Copy link
Author

mfb2 commented Feb 12, 2019

I am still interested in the feature. I had been waiting for this PR to be reviewed since September last year, and my project has since moved on. We had to fork this project at an earlier version in order to finish our work. I do not have time to address the volume of feedback as I am no longer funded to do development on this product.

I believe in open source and a vibrant community of contributions; our challenge here appears to be in aligning expectations. When resources permit, I will push an update to incorporate your feedback. In the meantime, I encourage you to leverage my contribution as a quick-start for completing the feature.

@cocojoe
Copy link
Member

cocojoe commented Feb 25, 2019

@mfb2 We appreciate your contribution, apologise for the time taken to action it originally. I agree it may make a good quick-start as you put it. However, we have a lot of repos and don't typically pick up and continue another authors work unless it aligns with our own priorities. I'm happy to leave this PR open for a while as it would be great to see you complete your contribution 👍

@cocojoe
Copy link
Member

cocojoe commented Oct 10, 2019

Closed due to inactivity.

@cocojoe cocojoe closed this Oct 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants