Skip to content

[Syntax Review] Additional types of SampleGen code samples - #7051

Closed
beccasaurus wants to merge 1 commit into
googleapis:masterfrom
beccasaurus:samplegen-additional-sample-types-for-review
Closed

beccasaurus wants to merge 1 commit into
googleapis:masterfrom
beccasaurus:samplegen-additional-sample-types-for-review

Conversation

@beccasaurus

@beccasaurus beccasaurus commented Dec 17, 2019 •

Copy link
Copy Markdown

Follow-up code review PR –

To evaluate the readiness of ☕️ Java's GAPIC sample generator for use on cloud.google.com,
this provides a full set of samples which demonstrate the basic features of generated samples.

The .yaml sample config files for these samples can all be found here: .yaml sample configs

The first review Pull Request contained samples to show the basic structure as well as LRO samples.

This contains samples demonstrating those features and a few more use-cases for review –

Sample Description
SamplegenBasics.java Shows basic structure/anatomy of Java samples
SamplegenNoConfig.java Minimal sample example (zero configuration)
SamplegenNoResponse.java API method which returns Empty
SamplegenPaged.java Paged API method (iterates over pages of results)
SamplegenResourcePath.java Sample which requires user's Google Project ID
SamplegenMapFieldAccess.java Access map value by key and enumerate keys/values
SamplegenReadAndWriteFiles.java Read binary file into bytes & write string to local file
SamplegenRepeatedFields.java Repeated request field & show loop/access by index
SamplegenWriteBytesToFile.java Write bytes field in response to local file
SamplegenLro.java Call long-running operation API endpoint

This Pull Request is focused on feedback collection & a similar PR is being sent to all languages.

Once you've completed your review and left comments, you may feel free to close the PR!

We will scope out making all of the requested changes and reach out to language team offline.

🙏 Thanks in advance for your review!


P.S. Nits welcome! We'd like to get these as polished as possible for usage on cloud.google.com.

Please feel free to note in comments whether something is: 🛑 Blocker -vs- 📌 Nit


🔍 Known Issues

  • Switch commented out placeholder variables => provide an overload with defaults
  • ❓ Possible to add link to available types for enums?
  • When using resources, add a comment with try block explaining importance
  • ❓ Exception handling – can we show something better than catch (Exception)?
  • Remove “DO NOT EDIT” / soften language so that developers are the target audience

🔍 Tool for review: Java SAMPLE_FORMAT

📑 (internal)

Note: this is using the same sample .snip templates as with the previous PR
#6543
@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Dec 17, 2019
@beccasaurus

Copy link
Copy Markdown
Author

Hello Java friends ☕️

Follow-up PR, as promised!

@kurtisvg @averikitsch @lesv @chingor13 @googleapis/samplegen

@beccasaurus

Copy link
Copy Markdown
Author

Note: pulling this together required generating samples from a variety of different APIs, hence PR being sent to google-cloud-java rather than to the individual java-* repos

@@ -0,0 +1,59 @@
/*

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ℹ️ Note: this "no config" sample is different from the rest. Rather than demonstrating a feature, it shows what a sample looks like when not configured at all.

These samples could be generated for every API method without any manual configuration.

Review of the "no config" sample is not required, this is more of an FYI :)

Comment on lines +36 to +41
* Please include the following imports to run this sample.
*
* import com.google.cloud.vision.v1.CreateProductSetRequest;
* import com.google.cloud.vision.v1.LocationName;
* import com.google.cloud.vision.v1.ProductSearchClient;
* import com.google.cloud.vision.v1.ProductSet;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We've already provided feedback - strike the comments.

* import com.google.cloud.vision.v1.ProductSet;
*/

public static void sampleCreateProductSet() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This sample has a constructor, but most do not?

// sample-metadata:
// title: This is the sample title
// description: This is the sample description
// usage: gradle run -PmainClass=com.google.cloud.examples.vision.v1.SamplegenBasics [--args='[--display_name "This is the default value of the display_name request field"]']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I thought we typically used maven in samples?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the goal is that we should support both - but since for Java we expect most users to run these from the IDE (instead of command line) these instructions should be something along the lines of "Update the values under the TODO(developer) line and execute the function to run this sample).

Comment on lines +50 to +54
/**
* This is the sample description
*
* @param displayName Description of the parameter
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Believe no javadocs.
Also since the constructor has the displayName variable this is redundant.

@codecov

codecov Bot commented Dec 17, 2019 •

Copy link
Copy Markdown

Codecov Report

Merging #7051 into master will decrease coverage by <.01%.
The diff coverage is n/a.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #7051      +/-   ##
============================================
- Coverage     32.18%   32.18%   -0.01%     
+ Complexity     3698     3697       -1     
============================================
  Files           249      249              
  Lines         40371    40371              
  Branches       6134     6134              
============================================
- Hits          12994    12992       -2     
- Misses        26304    26305       +1     
- Partials       1073     1074       +1
Impacted Files Coverage Δ Complexity Δ
...oogle/cloud/spanner/jdbc/SingleUseTransaction.java 86% <0%> (-1%) 36% <0%> (-1%)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8befeef...8d3e58c. Read the comment docs.

* @param displayName Description of the parameter
*/
public static void sampleCreateProductSet(String displayName) {
try (ProductSearchClient productSearchClient = ProductSearchClient.create()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe we are also adding this comment to all client declarations:

// Initialize client that will be used to send requests. This client only needs to be created
// once, and can be reused for multiple requests. After completing all of your requests, call
// the "close" method on the client to safely clean up any remaining background resources.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I love this! +💯 Thanks for providing the exact comment!

try (ProductSearchClient productSearchClient = ProductSearchClient.create()) {

// The project and location in which the product set should be created.
LocationName parent = LocationName.of("[PROJECT]", "[LOCATION]");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Anything the user needs to set should be in the constructor

import com.google.cloud.vision.v1.ProductSearchClient;
import com.google.cloud.vision.v1.ProductSetName;

public class SamplegenNoResponse {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No response or empty response?

try (ProductSearchClient productSearchClient = ProductSearchClient.create()) {

// The project and location in which the product set should be created.
LocationName parent = LocationName.of("[PROJECT]", "[LOCATION]");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit - I wish we used EnvVar's for this rather than user editing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There should probably be TODO(developer) here as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These would be good to pass in as arguments, so we can test them.


public static void sampleCreateProductSet() {
// TODO(developer): Replace these variables before running the sample.
String displayName = "This is the default value of the display_name request field";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit - wish this was an EnvVar

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I disagree on this one - if we expect users to copy/paste, it makes more sense that they would just update the values in the IDE after doing so.

It makes sense that the tests would look for EnvVars though.

Comment on lines +70 to +72
} catch (Exception exception) {
System.err.println("Failed to create the client due to: " + exception);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

BLOCK - I think we've already provided feedback on this.
Exceptions should be as specific as possible and suggest what to do. Just printing the error isn't good form.
https://google.github.io/styleguide/javaguide.html#s6.2-caught-exceptions

public static void main(String[] args) throws Exception {
Options options = new Options();
options.addOption(
Option.builder("").required(false).hasArg(true).longOpt("display_name").build());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

BLOCK - "" ? This doesn't tell me much.
If I was reviewing, I'd certainly ask for a comment.

}
// [END samplegen_basics]

public static void main(String[] args) throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

BLOCK - Exceptions should ideally be very specific if thrown to tell the user what to expect.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additionally - ArgParsing should be ideally outside the class (but at the very least excluded from the snippets).

public static void sampleCreateProductSet(String project) {
try (ProductSearchClient productSearchClient = ProductSearchClient.create()) {
LocationName parent = LocationName.of(project, "us-central1");
String displayName = "[DISPLAY NAME]";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit - would prefer EnvVar or as a passed param.

Comment on lines +69 to +71
} catch (Exception exception) {
System.err.println("Failed to create the client due to: " + exception);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

BLOCK - should be specific exception(s).


public static void main(String[] args) throws Exception {
Options options = new Options();
options.addOption(Option.builder("").required(false).hasArg(true).longOpt("project").build());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should explain ""

Comment on lines +36 to +48
/*
* Please include the following imports to run this sample.
*
* import com.google.cloud.texttospeech.v1.AudioConfig;
* import com.google.cloud.texttospeech.v1.AudioEncoding;
* import com.google.cloud.texttospeech.v1.SynthesisInput;
* import com.google.cloud.texttospeech.v1.SynthesizeSpeechRequest;
* import com.google.cloud.texttospeech.v1.SynthesizeSpeechResponse;
* import com.google.cloud.texttospeech.v1.TextToSpeechClient;
* import com.google.cloud.texttospeech.v1.VoiceSelectionParams;
* import java.io.FileOutputStream;
* import java.io.OutputStream;
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit - STRIKE

Comment on lines +71 to +73
} catch (Exception exception) {
System.err.println("Failed to create the client due to: " + exception);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

BLOCK - needs to be the specific ERROR's so user knows to write code to deal with the situation, or don't catch them and add exception to the method defn.

try (ProductSearchClient productSearchClient = ProductSearchClient.create()) {

// The project and location in which the product set should be created.
LocationName parent = LocationName.of("[PROJECT]", "[LOCATION]");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These would be good to pass in as arguments, so we can test them.

ProductSet productSet = response;
System.out.printf("The full name of the created product set: %s\n", productSet.getName());
} catch (Exception exception) {
System.err.println("Failed to create the client due to: " + exception);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Question to the Java DPEs: Should we be using a better logging library like slf4j?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In the sample format, we already suggest using java.util.logging for web applications - slf4j matches this interface I think?

Maybe just expand this to suggesting for all samples?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(this is probably a nit)

String language = "en";

// The text content to analyze
String content = "Googleplex is at 1600 Amphitheatre Parkway, Mountain View, CA.";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Question for DPEs: a lot of the code in these samples feel squished together. Is that because of the formatting of builders?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IMO - there should be a breaks between building, sending/receiving, and acting on the response (Arrange, Act, Assert)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@beccasaurus, does samplegen support adding empty lines yet?

// sample-metadata:
// title: This is the sample title
// description: This is the sample description
// usage: gradle run -PmainClass=com.google.cloud.examples.vision.v1.SamplegenBasics [--args='[--display_name "This is the default value of the display_name request field"]']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the goal is that we should support both - but since for Java we expect most users to run these from the IDE (instead of command line) these instructions should be something along the lines of "Update the values under the TODO(developer) line and execute the function to run this sample).


public static void sampleCreateProductSet() {
// TODO(developer): Replace these variables before running the sample.
String displayName = "This is the default value of the display_name request field";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I disagree on this one - if we expect users to copy/paste, it makes more sense that they would just update the values in the IDE after doing so.

It makes sense that the tests would look for EnvVars though.

// The API response represents the created product set
ProductSet productSet = response;
System.out.printf("The full name of the created product set: %s\n", productSet.getName());
} catch (Exception exception) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should catch a more specific Exception - See Exception Handling

ProductSet productSet = response;
System.out.printf("The full name of the created product set: %s\n", productSet.getName());
} catch (Exception exception) {
System.err.println("Failed to create the client due to: " + exception);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In the sample format, we already suggest using java.util.logging for web applications - slf4j matches this interface I think?

Maybe just expand this to suggesting for all samples?

}
// [END samplegen_basics]

public static void main(String[] args) throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additionally - ArgParsing should be ideally outside the class (but at the very least excluded from the snippets).

RecognitionAudio audio = RecognitionAudio.newBuilder().setUri(uri).build();
LongRunningRecognizeRequest request =
LongRunningRecognizeRequest.newBuilder().setConfig(config).setAudio(audio).build();
OperationFuture<LongRunningRecognizeResponse, LongRunningRecognizeMetadata> future =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: Ideally there would be a break between the section that builds the request and the section that sends the request - See Arrange, Act, Assert

Additionally, I think it would be helpful to include a comment explaining that the request is only to start the operation, which is why the future.get is called later.

/** This sample reads and loops over a map field in the response */
public static void sampleAnalyzeEntities() {
try (LanguageServiceClient languageServiceClient = LanguageServiceClient.create()) {
Document.Type type = Document.Type.PLAIN_TEXT;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar to my comment in Python, we should provide links to docs or page on what values are supported.

String language = "en";

// The text content to analyze
String content = "Googleplex is at 1600 Amphitheatre Parkway, Mountain View, CA.";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

IMO - there should be a breaks between building, sending/receiving, and acting on the response (Arrange, Act, Assert)

for (Map.Entry<String, String> entry : entity.getMetadataMap().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
System.out.printf("%s: %s\n", key, value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: inline single use variables

Suggested change
System.out.printf("%s: %s\n", key, value);
System.out.printf("%s: %s\n", entry.getKey(), entry.getValue());

}

// Loop over just keys:
for (String theKey : entity.getMetadataMap().keySet()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar to python - these two loops don't show any API interaction with the library, they are only showing basic Java syntax. Since we've already established it's a map in the first loop, these two feel unnecessary.

@chingor13 chingor13 closed this Dec 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes This human has signed the Contributor License Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants