Skip to content

Commit 0db3a51

Browse files
yihanzhenbusunkim96
authored andcommitted
SampleGen: default calling forms (googleapis#2713)
When: ```yaml - samples: standalone: - region_tag: some_tag # unspecified calling forms value_sets: a_value_set, b_value_set ``` Default calling forms are used. Also use region tags as file names when there is only one sample that has this region tag.
1 parent 5addb73 commit 0db3a51

26 files changed

+1418
-510
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/* Copyright 2019 Google LLC
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
package com.google.api.codegen.config;
16+
17+
import com.google.api.codegen.SampleValueSet;
18+
import com.google.api.codegen.viewmodel.CallingForm;
19+
import com.google.auto.value.AutoValue;
20+
21+
/** SampleConfig represents configurations of a sample. */
22+
@AutoValue
23+
public abstract class SampleConfig {
24+
25+
public abstract String regionTag();
26+
27+
public abstract CallingForm callingForm();
28+
29+
public abstract SampleValueSet valueSet();
30+
31+
public abstract SampleSpec.SampleType type();
32+
33+
public static SampleConfig create(
34+
String regionTag,
35+
CallingForm callingForm,
36+
SampleValueSet valueSet,
37+
SampleSpec.SampleType type) {
38+
return newBuilder()
39+
.regionTag(regionTag)
40+
.callingForm(callingForm)
41+
.valueSet(valueSet)
42+
.type(type)
43+
.build();
44+
}
45+
46+
public static Builder newBuilder() {
47+
return new AutoValue_SampleConfig.Builder();
48+
}
49+
50+
@AutoValue.Builder
51+
public abstract static class Builder {
52+
53+
public abstract Builder regionTag(String val);
54+
55+
public abstract Builder callingForm(CallingForm val);
56+
57+
public abstract Builder valueSet(SampleValueSet val);
58+
59+
public abstract Builder type(SampleSpec.SampleType val);
60+
61+
public abstract SampleConfig build();
62+
}
63+
}

src/main/java/com/google/api/codegen/config/SampleSpec.java

+66-28
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
import com.google.api.codegen.viewmodel.CallingForm;
2323
import com.google.auto.value.AutoValue;
2424
import java.util.ArrayList;
25+
import java.util.Collections;
2526
import java.util.HashSet;
2627
import java.util.List;
28+
import java.util.function.Function;
2729
import java.util.stream.Collectors;
2830
import javax.annotation.Nullable;
2931

@@ -112,40 +114,76 @@ private static boolean expressionMatchesId(String expression, String id) {
112114
}
113115

114116
/**
115-
* Returns the SampleValueSets that were specified for this methodForm and sampleType.
117+
* Matches all the IDs within `targets` that match one or more elements of `expressions`. The IDs
118+
* are extracted from elements of targets via `targetToId`."
119+
*/
120+
private static <T> List<T> expressionsMatchIds(
121+
List<String> expressions, List<T> targets, Function<T, String> targetToId) {
122+
return targets
123+
.stream()
124+
.filter(
125+
t ->
126+
expressions.stream().anyMatch(exp -> expressionMatchesId(exp, targetToId.apply(t))))
127+
.collect(Collectors.toList());
128+
}
129+
130+
/**
131+
* Returns all the valid combinations of calling forms of value sets.
116132
*
117-
* @param methodForm The calling form for which value sets are requested
118-
* @param sampleType The sample type for which value sets are requested
119-
* @return A set of SampleValueSets for methodForm andSampleType
133+
* <p>We match calling forms specified by users with `allValidCallingForms`. If users did not
134+
* specify any calling forms, we match `defaultCallingForm` with `allValidCallingForms`. If we
135+
* found no matching calling forms, an empty list will be returned. Note this implies that
136+
* `defaultCallingForm` can match none of the calling forms in `allValidCallingForms`. For
137+
* example, the `defaultCallingForm` for a C# unary call is `Request`, but it's not a valid
138+
* calling form when generating asynchronous samples using the generated asynchonous public
139+
* method. In this case we will not generate samples for this method.
140+
*
141+
* <p>We match value sets specified by users with `this.values`. If we find no matching value
142+
* sets, we use the default one. For incode samples, we always use the default value set derived
143+
* from sample_code_init_fields for backward compatibility.
144+
*
145+
* <p>We should probably disable default value sets after incode samples stop using
146+
* `sample_code_init_fields`.
120147
*/
121-
public List<ValueSetAndTags> getMatchingValueSets(CallingForm methodForm, SampleType sampleType) {
122-
String methodFormString = Name.anyCamel(methodForm.toString()).toLowerUnderscore();
123-
124-
// Get the `SampleTypeConfigs` configured for this `methodForm`.
125-
List<SampleTypeConfiguration> matchingSamples =
126-
getConfigFor(sampleType)
127-
.stream()
128-
.filter(
129-
sampleConfig ->
130-
sampleConfig
131-
.getCallingFormsList()
132-
.stream()
133-
.anyMatch(expression -> expressionMatchesId(expression, methodFormString)))
134-
.collect(Collectors.toList());
135-
136-
// Construct a `ValueSetAndTags` for each sample specified in each element of `matchingSamples`.
137-
List<ValueSetAndTags> result = new ArrayList<>();
138-
for (SampleValueSet vset : valueSets) {
139-
for (SampleTypeConfiguration sample : matchingSamples) {
140-
for (String valueSetExpression : sample.getValueSetsList()) {
141-
if (expressionMatchesId(valueSetExpression, vset.getId())) {
142-
result.add(
143-
ValueSetAndTags.newBuilder().values(vset).regionTag(sample.getRegionTag()).build());
148+
public List<SampleConfig> getSampleConfigs(
149+
List<CallingForm> allValidCallingForms,
150+
CallingForm defaultCallingForm,
151+
SampleValueSet defaultValueSet,
152+
SampleType type) {
153+
List<SampleConfig> sampleConfigs = new ArrayList<>();
154+
if (type == SampleType.EXPLORER) {
155+
throw new UnsupportedOperationException("API Explorer samples unimplemented yet.");
156+
} else if (type == SampleType.IN_CODE) {
157+
for (CallingForm form : allValidCallingForms) {
158+
sampleConfigs.add(SampleConfig.create("", form, defaultValueSet, type));
159+
}
160+
} else {
161+
for (SampleTypeConfiguration config : getConfigFor(type)) {
162+
List<CallingForm> matchingCallingForms = null;
163+
List<SampleValueSet> matchingValueSets = null;
164+
165+
List<String> callingFormNames =
166+
config.getCallingFormsList().isEmpty()
167+
? Collections.singletonList(
168+
Name.anyCamel(defaultCallingForm.toString()).toLowerUnderscore())
169+
: config.getCallingFormsList();
170+
matchingCallingForms =
171+
expressionsMatchIds(
172+
callingFormNames, allValidCallingForms, CallingForm::toLowerUnderscore);
173+
174+
matchingValueSets =
175+
expressionsMatchIds(config.getValueSetsList(), valueSets, v -> v.getId());
176+
177+
for (CallingForm form : matchingCallingForms) {
178+
for (SampleValueSet matchingValueSet : matchingValueSets) {
179+
sampleConfigs.add(
180+
SampleConfig.create(config.getRegionTag(), form, matchingValueSet, type));
144181
}
145182
}
146183
}
147184
}
148-
return result;
185+
186+
return sampleConfigs;
149187
}
150188

151189
/** Returns the single {@code SampleTypeConfiguration} for the specified {@code sampleType}. */

src/main/java/com/google/api/codegen/transformer/SampleFileRegistry.java

+55-1
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,78 @@
1414
*/
1515
package com.google.api.codegen.transformer;
1616

17+
import com.google.api.codegen.viewmodel.MethodSampleView;
1718
import com.google.auto.value.AutoValue;
19+
import com.google.common.base.Preconditions;
1820
import java.util.HashMap;
21+
import java.util.List;
1922
import java.util.Map;
2023

2124
/**
2225
* {@code SampleFileRegistry} is used to verify that the samples we generate with different
2326
* parameters have different paths, so they don't clobber each other.
27+
*
28+
* <p>If a sample has a unique region tag, the file name of the sample will be its region tag in the
29+
* language-idiomatic case, followed by an appropriate extension.
30+
*
31+
* <p>If the region tag of a sample is not unique, the file name of the sample will be constructed
32+
* by concatinating `method_name`, `calling_form` and `value_set_id`. The file name will be in the
33+
* language-idiomatic case and followed by an appropriate extension as well.
2434
*/
2535
public class SampleFileRegistry {
2636

2737
private final Map<String, SampleInfo> files = new HashMap<>();
38+
private final Map<String, Integer> regionTagCount = new HashMap<>();
39+
private final SurfaceNamer namer;
40+
41+
public SampleFileRegistry(SurfaceNamer namer, List<MethodSampleView> allSamples) {
42+
this.namer = namer;
43+
for (MethodSampleView sample : allSamples) {
44+
regionTagCount.put(
45+
sample.regionTag(), regionTagCount.getOrDefault(sample.regionTag(), 0) + 1);
46+
}
47+
}
48+
49+
public String getSampleClassName(MethodSampleView sample, String method) {
50+
String regionTag = sample.regionTag();
51+
Preconditions.checkState(
52+
regionTagCount.get(regionTag) != null && regionTagCount.get(regionTag) > 0,
53+
"Sample not registered.");
54+
if (regionTagCount.get(regionTag) == 1) {
55+
return namer.getApiSampleClassName(regionTag);
56+
} else {
57+
String callingForm = sample.callingForm().toLowerCamel();
58+
String valueSet = sample.valueSet().id();
59+
return namer.getApiSampleClassName(
60+
method, sample.callingForm().toLowerUnderscore(), sample.valueSet().id());
61+
}
62+
}
63+
64+
public String getSampleFileName(MethodSampleView sample, String method) {
65+
String regionTag = sample.regionTag();
66+
String callingForm = sample.callingForm().toLowerCamel();
67+
String valueSet = sample.valueSet().id();
68+
Preconditions.checkState(
69+
regionTagCount.get(regionTag) != null && regionTagCount.get(regionTag) > 0,
70+
"Sample not registered.");
71+
String fileName;
72+
if (regionTagCount.get(regionTag) == 1) {
73+
fileName = namer.getApiSampleFileName(regionTag);
74+
} else {
75+
fileName =
76+
namer.getApiSampleFileName(
77+
method, sample.callingForm().toLowerUnderscore(), sample.valueSet().id());
78+
}
79+
addFile(fileName, method, callingForm, valueSet, regionTag);
80+
return fileName;
81+
}
2882

2983
/**
3084
* Adds a file with the given parameters to the registry. If a file with the given {@code path}
3185
* previously existed in the registry and any of the parameters don't match, throws an exception
3286
* describing the conflict.
3387
*/
34-
public void addFile(
88+
private void addFile(
3589
String path, String method, String callingForm, String valueSet, String regionTag) {
3690
SampleInfo current =
3791
SampleInfo.newBuilder()

src/main/java/com/google/api/codegen/transformer/SampleTransformer.java

+30-47
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import com.google.api.codegen.config.MethodConfig;
2323
import com.google.api.codegen.config.MethodContext;
2424
import com.google.api.codegen.config.OutputContext;
25+
import com.google.api.codegen.config.SampleConfig;
2526
import com.google.api.codegen.config.SampleParameterConfig;
2627
import com.google.api.codegen.config.SampleSpec.SampleType;
27-
import com.google.api.codegen.config.SampleSpec.ValueSetAndTags;
2828
import com.google.api.codegen.metacode.InitCodeContext;
2929
import com.google.api.codegen.metacode.InitCodeContext.InitCodeOutputType;
3030
import com.google.api.codegen.util.Name;
@@ -202,42 +202,34 @@ public List<MethodSampleView> generateSamples(
202202
InitCodeOutputType initCodeOutputType,
203203
List<CallingForm> callingForms) {
204204

205+
CallingForm defaultCallingForm = methodContext.getNamer().getDefaultCallingForm(methodContext);
205206
List<MethodSampleView> methodSampleViews = new ArrayList<>();
206207
MethodConfig methodConfig = methodContext.getMethodConfig();
207-
ImmutableList<ValueSetAndTags> defaultValueSets = defaultValueSets(methodConfig);
208-
for (CallingForm form : callingForms) {
209-
List<ValueSetAndTags> matchingValueSets =
210-
methodConfig.getSampleSpec().getMatchingValueSets(form, sampleType());
211-
212-
if (sampleType() == SampleType.IN_CODE
213-
|| !methodConfig.getSampleSpec().isConfigured()
214-
|| matchingValueSets.isEmpty()) {
215-
matchingValueSets = defaultValueSets;
216-
}
217-
218-
for (ValueSetAndTags setAndTag : matchingValueSets) {
219-
// Don't overwrite the initContext in outer scope.
220-
InitCodeContext thisContext = initContext;
221-
SampleValueSet valueSet = setAndTag.values();
222-
if (thisContext == null) {
223-
thisContext =
224-
createInitCodeContext(methodContext, fieldConfigs, initCodeOutputType, valueSet);
225-
}
226-
methodSampleViews.add(generateSample(setAndTag, form, methodContext, thisContext));
208+
SampleValueSet defaultValueSet = defaultValueSet(methodConfig);
209+
210+
for (SampleConfig sampleConfig :
211+
methodConfig
212+
.getSampleSpec()
213+
.getSampleConfigs(callingForms, defaultCallingForm, defaultValueSet, sampleType())) {
214+
InitCodeContext thisContext = initContext; // Do not override outer initContext
215+
if (thisContext == null) {
216+
thisContext =
217+
createInitCodeContext(
218+
methodContext, fieldConfigs, initCodeOutputType, sampleConfig.valueSet());
227219
}
220+
methodSampleViews.add(generateSample(sampleConfig, methodContext, thisContext));
228221
}
229222
return methodSampleViews;
230223
}
231224

232225
private MethodSampleView generateSample(
233-
ValueSetAndTags setAndTag,
234-
CallingForm form,
235-
MethodContext methodContext,
236-
InitCodeContext initCodeContext) {
226+
SampleConfig config, MethodContext methodContext, InitCodeContext initCodeContext) {
237227
methodContext = methodContext.cloneWithEmptyTypeTable();
238228
InitCodeView initCodeView =
239229
initCodeTransformer().generateInitCode(methodContext, initCodeContext);
240-
SampleValueSet valueSet = setAndTag.values();
230+
SampleValueSet valueSet = config.valueSet();
231+
CallingForm form = config.callingForm();
232+
String regionTag = config.regionTag();
241233
List<OutputSpec> outputs = valueSet.getOnSuccessList();
242234
if (outputs.isEmpty()) {
243235
outputs = OutputTransformer.defaultOutputSpecs(methodContext);
@@ -280,10 +272,7 @@ private MethodSampleView generateSample(
280272
.sampleImports(sampleImportSectionView)
281273
.regionTag(
282274
regionTagFromSpec(
283-
setAndTag.regionTag(),
284-
methodContext.getMethodModel().getSimpleName(),
285-
form,
286-
valueSet.getId()))
275+
regionTag, methodContext.getMethodModel().getSimpleName(), form, valueSet.getId()))
287276
.sampleFunctionName(
288277
methodContext.getNamer().getSampleFunctionName(methodContext.getMethodModel()))
289278
.sampleFunctionDoc(sampleFunctionDocView)
@@ -323,28 +312,22 @@ private InitCodeContext createInitCodeContext(
323312
.build();
324313
}
325314

326-
private ImmutableList<ValueSetAndTags> defaultValueSets(MethodConfig methodConfig) {
315+
private SampleValueSet defaultValueSet(MethodConfig methodConfig) {
327316
// For backwards compatibility in the configs, we need to use sample_code_init_fields instead
328317
// to generate the samples in various scenarios. Once all the configs have been migrated to
329318
// use the SampleSpec, we can delete the code below as well as sample_code_init_fields.
330319
String defaultId =
331320
(sampleType() == SampleType.IN_CODE) ? "sample_code_init_field" : INIT_CODE_SHIM;
332-
ImmutableList<ValueSetAndTags> defaultValueSets =
333-
ImmutableList.of(
334-
ValueSetAndTags.newBuilder()
335-
.values(
336-
SampleValueSet.newBuilder()
337-
.setParameters(
338-
SampleParameters.newBuilder()
339-
.addAllDefaults(methodConfig.getSampleCodeInitFields())
340-
.build())
341-
.setId(defaultId)
342-
.setDescription("value set imported from sample_code_init_fields")
343-
.setTitle("Sample Values")
344-
.build())
345-
.regionTag("")
346-
.build());
347-
return defaultValueSets;
321+
322+
return SampleValueSet.newBuilder()
323+
.setParameters(
324+
SampleParameters.newBuilder()
325+
.addAllDefaults(methodConfig.getSampleCodeInitFields())
326+
.build())
327+
.setId(defaultId)
328+
.setDescription("value set imported from sample_code_init_fields")
329+
.setTitle("Sample Values")
330+
.build();
348331
}
349332

350333
private ImmutableMap<String, SampleParameterConfig> sampleParamConfigMapFromValueSet(

0 commit comments

Comments
 (0)