Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

docs(samples): add UpdateUserEventsJson implementation #388

Closed
wants to merge 23 commits into from

Conversation

sborisenkox
Copy link
Contributor

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> ☕️

If you write sample code, please follow the samples format.

@sborisenkox sborisenkox requested review from a team as code owners March 30, 2022 10:27
@product-auto-label product-auto-label bot added the api: retail Issues related to the googleapis/java-retail API. label Mar 30, 2022
@kweinmeister kweinmeister added the owlbot:run Add this label to trigger the Owlbot post processor. label Mar 30, 2022
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Mar 30, 2022
@kweinmeister kweinmeister added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Mar 30, 2022
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Mar 30, 2022
@Neenu1995 Neenu1995 closed this Mar 30, 2022
@Neenu1995 Neenu1995 reopened this Mar 30, 2022
String invalidFilePath = "src/main/resources/user_events_some_invalid.json";

updateEventsTimestamp(filePath);
updateEventsTimestamp(invalidFilePath);
Copy link
Contributor

@Neenu1995 Neenu1995 Mar 30, 2022

Choose a reason for hiding this comment

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

Does the test for these already exist?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are no tests for classes which in 'setup' package.

Choose a reason for hiding this comment

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

  1. These samples need tests added.
  2. I really dislike this pattern of relying the main to test. It's inconsistent with how the other java samples are tested, and showing both a valid and an invalid example just makes the example more confusing.

Copy link
Contributor Author

@sborisenkox sborisenkox Apr 1, 2022

Choose a reason for hiding this comment

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

Could you please clarify, what exactly issue with the tests you observe?
Currently, all the tests are relying on the pattern you recommended on past PR’s.
#303 (comment)

Copy link

Choose a reason for hiding this comment

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

I did not recommend using the main for testing. In fact, I explictly recommended against it: #303 (comment)

Here's example of how it's typically handled (calling the snippet directly): https://github.com/GoogleCloudPlatform/java-docs-samples/blob/e1ec7d8800f83db0bc7dd3b6f02cfa634802ec52/kms/src/main/java/kms/CreateKeyHsm.java#L41

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean to remove main method calls from unit tests? Just call methods directly?

Copy link

Choose a reason for hiding this comment

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

@sborisenkox Yes.

This lets you test for different conditions individually (one test for filepath and one for invalidFilePath) and is less confusing for users to navigate.

Copy link
Contributor Author

@sborisenkox sborisenkox Apr 5, 2022

Choose a reason for hiding this comment

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

  1. These samples need tests added.
  2. I really dislike this pattern of relying the main to test. It's inconsistent with how the other java samples are tested, and showing both a valid and an invalid example just makes the example more confusing.
  1. Tests for the setup package were added.
  2. Unit tests and some classes were refactored.

Please review.

@kweinmeister kweinmeister changed the title Add UpdateUserEventsJson impl. docs(samples): add UpdateUserEventsJson implementation Mar 30, 2022
@sborisenkox sborisenkox requested a review from Neenu1995 March 31, 2022 07:32
@product-auto-label product-auto-label bot added the samples Issues that are directly related to samples. label Mar 31, 2022
String invalidFilePath = "src/main/resources/user_events_some_invalid.json";

updateEventsTimestamp(filePath);
updateEventsTimestamp(invalidFilePath);

Choose a reason for hiding this comment

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

  1. These samples need tests added.
  2. I really dislike this pattern of relying the main to test. It's inconsistent with how the other java samples are tested, and showing both a valid and an invalid example just makes the example more confusing.

@sborisenkox sborisenkox requested a review from kurtisvg April 4, 2022 15:16
@Neenu1995 Neenu1995 added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 5, 2022
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 5, 2022
@Neenu1995 Neenu1995 added the owlbot:run Add this label to trigger the Owlbot post processor. label Apr 5, 2022
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Apr 5, 2022
@Neenu1995 Neenu1995 added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 6, 2022
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 6, 2022
Copy link

@kurtisvg kurtisvg left a comment

Choose a reason for hiding this comment

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

Please take some time to review the Sample Style Guide. And look at some example snippets. Here's a good example of a snippet following the style guide:

https://github.com/GoogleCloudPlatform/java-docs-samples/blob/main/kms/src/main/java/kms/CreateKeyAsymmetricDecrypt.java

Comment on lines 45 to 55
String bucketName = System.getenv("EVENTS_BUCKET_NAME");
String gcsBucket = String.format("gs://%s", bucketName);
String gcsErrorsBucket = String.format("%s/error", gcsBucket);
String gcsUserEventsObject = "user_events.json";
// TO CHECK ERROR HANDLING USE THE JSON WITH INVALID USER EVENT:
// gcsEventsObject = "user_events_some_invalid.json"

ImportUserEventsRequest importEventsGcsRequest =
getImportEventsGcsRequest(gcsUserEventsObject, gcsBucket, gcsErrorsBucket, defaultCatalog);
importUserEventsFromGcs(importEventsGcsRequest);
}
Copy link

Choose a reason for hiding this comment

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

The main function should be example values only. See Method Structure in the Samples Style Guide.

It should look something like:

public static void main(String[] args) {
    // TODO(developer): Replace these variables before running the sample.
    String catalog = String.format(
            "projects/%s/locations/global/catalogs/%s", "your-project-id" "your-catalog-name")
    // Path to a file containing UserEvents to import in a GCS bucket
    String gcsFile = String.format("gs://your-bucket-name/path/to/file.json")
    importUserEventsFromGcs(catalog, gcsFile);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.


while (!operation.getDone()) {
System.out.println("Please wait till operation is done.");
Thread.sleep(30_000);
Copy link

Choose a reason for hiding this comment

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

Please use the java.time package here.

Suggested change
Thread.sleep(30_000);
TimeUnit.SECONDS.sleep(30);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed.

OperationsClient operationsClient = serviceClient.getOperationsClient();
Operation operation = operationsClient.getOperation(operationName);

while (!operation.getDone()) {
Copy link

Choose a reason for hiding this comment

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

I would recommend adding a timeout of some form to this loop so it cannot continue indefinitely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

System.out.printf("Operation result: %s%n", response);
}
} catch (InvalidArgumentException e) {
try (UserEventServiceClient serviceClient = UserEventServiceClient.create()) {
Copy link

Choose a reason for hiding this comment

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

Make sure to include the comment regarding client best practices.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 57 to 81
public static ImportUserEventsRequest getImportEventsGcsRequest(
String gcsEventsObject, String gcsBucket, String gcsErrorsBucket, String defaultCatalog) {
GcsSource gcsSource =
GcsSource.newBuilder()
.addInputUris(String.format("%s/%s", gcsBucket, gcsEventsObject))
.build();

UserEventInputConfig inputConfig =
UserEventInputConfig.newBuilder().setGcsSource(gcsSource).build();

System.out.println("GRS source: " + gcsSource.getInputUrisList());

importUserEventsFromGcs(gcsEventsObject, defaultCatalog);
ImportErrorsConfig errorsConfig =
ImportErrorsConfig.newBuilder().setGcsPrefix(gcsErrorsBucket).build();

ImportUserEventsRequest importRequest =
ImportUserEventsRequest.newBuilder()
.setParent(defaultCatalog)
.setInputConfig(inputConfig)
.setErrorsConfig(errorsConfig)
.build();

System.out.printf("Import user events from google cloud source request: %s%n", importRequest);

return importRequest;
Copy link

Choose a reason for hiding this comment

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

This doesn't need to be a seperate method - we should only be adding additional methods if it improve readability. See Class Structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've changed some classes, but others should have methods calls to prepare snippet resources.

}
} catch (BigQueryException e) {
System.out.printf("Exception message: %s", e.getMessage());
} catch (InvalidArgumentException e) {
Copy link

Choose a reason for hiding this comment

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

When can this "InvalidArgumentException" happen? Can we add a try/catch for this exception just where it's relevant?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's relevant in that case when bucket has no specified file.
Also added PermissionDeniedException for case when env variable bucketName can't be read properly.

Comment on lines +30 to +34
Timestamp currentDate =
Timestamp.newBuilder()
.setSeconds(Instant.now().getEpochSecond())
.setNanos(Instant.now().getNano())
.build();
Copy link

Choose a reason for hiding this comment

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

This doesn't need to be in main (it should be part of the sample).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't need to create a new bucket every time when running the unit test.
In the code sample, bucket name contains timestamp.
In the test class, bucket has static name.

Comment on lines 121 to 122
"Given GCS input path was not found. %n%s%n"
+ "Please run CreateTestResources class to create resources.",
Copy link

Choose a reason for hiding this comment

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

User's typically copy/paste these samples to run. If they need a specific resource, we should explain to them how to make it in a comment at the top of the snippet.

Suggested change
"Given GCS input path was not found. %n%s%n"
+ "Please run CreateTestResources class to create resources.",
"The specified GCS file does not exist. Please make sure to create one following the instructions at the top of this file. ",

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Explanation and all setup instructions will be described in a separate space.
Comment was changed.


private static final String BUCKET_NAME =
String.format("%s_events_%s", PROJECT_ID, CURRENT_DATE.getSeconds());

public static void main(String... args) throws IOException {
Copy link

Choose a reason for hiding this comment

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

these have to be String[] to be proper main functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines +49 to +50
// TO CHECK ERROR HANDLING USE THE JSON WITH INVALID USER EVENT:
// gcsEventsObject = "user_events_some_invalid.json"
Copy link

Choose a reason for hiding this comment

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

This should just be a separate test in your tests file. You can have one where you call a valid file (e.g.importUimportUserEventsFromGcs("your-catalog", "user-events.json) and one where you call an invalid file (e.g.importUimportUserEventsFromGcs("your-catalog", "some-invalid-file.json). You shouldn't be depending on someone to edit a comment out, because that means it's not being run the in the CI.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

@product-auto-label product-auto-label bot added the size: xl Pull request size is extra large. label Apr 14, 2022
@sborisenkox sborisenkox requested a review from kurtisvg April 18, 2022 14:21
@kweinmeister kweinmeister added kokoro:force-run Add this label to force Kokoro to re-run the tests. owlbot:run Add this label to trigger the Owlbot post processor. labels Apr 20, 2022
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Apr 20, 2022
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 20, 2022
Copy link

@kurtisvg kurtisvg left a comment

Choose a reason for hiding this comment

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

Can you please refactor this into smaller CLs? There are 68 changed files here and a diff of +- 2,000 lines. This is way too much to review in a single CL.

@sborisenkox
Copy link
Contributor Author

sborisenkox commented Apr 25, 2022

Can you please refactor this into smaller CLs? There are 68 changed files here and a diff of +- 2,000 lines. This is way too much to review in a single CL.

I have split into 4 PR's.
#415
#417
#418
#419

I close this PR.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api: retail Issues related to the googleapis/java-retail API. samples Issues that are directly related to samples. size: xl Pull request size is extra large.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants