Skip to content

Commit 71ea41d

Browse files
authored
Add listExamples gradle task. (#453)
* Add listExamples gradle task. * Reworked task action to accomodate migration examples. * Add search option. * Fixed call from non-base directory * Fix package name printout in traverseDirectory. Add comments explaining searchTerm argument. * Minor groovy cleanup.
1 parent 100a980 commit 71ea41d

2 files changed

Lines changed: 197 additions & 36 deletions

File tree

buildSrc/src/main/groovy/com.google.api-ads.java-conventions.gradle

Lines changed: 170 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
* These are shared settings common to all Google Ads Java library subprojects.
1717
*/
1818

19+
import org.gradle.api.DefaultTask
20+
import groovy.io.FileType
21+
import groovy.io.FileVisitResult
22+
import java.util.regex.Matcher
23+
1924
plugins {
2025
id 'java-library'
2126
id 'maven-publish'
@@ -58,67 +63,212 @@ dependencies {
5863
testImplementation 'junit:junit:4.13.1'
5964
}
6065

61-
public class ExampleRunnerTask extends JavaExec {
66+
class ExampleRunnerTask extends JavaExec {
6267

63-
@Input
64-
public String basePackage = 'com.google.ads.googleads.examples.'
68+
// The base package for all examples.
69+
private String basePackage = 'com.google.ads.googleads.examples.'
6570

71+
// Hints for failed executions.
72+
private final String exampleInvocation = 'For example, ' +
73+
'\033[0;35m--example="basicoperations.GetCampaigns"\033[0m.\n' +
74+
'Run \033[0;35m./gradlew -q listExamples\033[0m to list all examples, or ' +
75+
'\033[0;35m./gradlew -q listExamples --subdirectory="basicoperations"\033[0m ' +
76+
'to list examples in a specific subdirectory.\n\n'
77+
78+
// The package.ClassName and any arguments required to launch an example,
79+
// for example "basicoperations.GetCampaigns --customerId 1234567890".
6680
@Optional
6781
@Input
6882
private String exampleArguments
6983

70-
public ExampleRunnerTask() {
84+
ExampleRunnerTask() {
7185
group = 'Execution'
7286
description = 'Run a Google Ads API example.'
7387
errorOutput = System.err
7488
}
7589

90+
String getBasePackage() {
91+
return this.basePackage
92+
}
93+
94+
void setBasePackage(String basePackage) {
95+
this.basePackage = basePackage
96+
}
97+
98+
String getExampleArguments() {
99+
return this.exampleArguments
100+
}
101+
76102
@Option(option = 'example', description = 'Sets the example to launch and' +
77-
' any arguments. Required for execution. E.g. ' +
78-
'"basicoperations.GetCampaigns --customerId 1234567890"')
79-
public void setExampleArguments(String exampleArguments) {
103+
' any arguments. Required for execution. For example, ' +
104+
'"basicoperations.GetCampaigns --customerId 1234567890"')
105+
void setExampleArguments(String exampleArguments) {
80106
this.exampleArguments = exampleArguments.trim()
81107
int firstSpaceIndex = this.exampleArguments.indexOf(' ')
82108

83109
// No additional arguments were passed, just the example name.
84110
if (firstSpaceIndex == -1) {
85-
main = basePackage + this.exampleArguments
111+
main = this.basePackage + this.exampleArguments
86112
}
87113
// Otherwise, separate the input and set the arguments to pass to the
88114
// main class.
89115
else {
90-
main = basePackage + this.exampleArguments[0..firstSpaceIndex - 1]
116+
main = this.basePackage + this.exampleArguments[0..firstSpaceIndex - 1]
91117
argsString(exampleArguments[(firstSpaceIndex + 1)..-1])
92118
}
93119
}
94120

95121
@TaskAction
96122
@Override
97-
public void exec() {
123+
void exec() {
98124
if (!(exampleArguments?.trim())) {
99125
throw new GradleException('\033[0;31mMissing example!\033[0m ' +
100-
'Please rerun with one provided, e.g. ' +
101-
'\033[0;35m--example="basicoperations.GetCampaigns"\033[0m')
126+
'Please rerun with one provided. ' + exampleInvocation)
102127
}
103128
logQuietMessage('Running example: ' + main + ', args: ' +
104-
args.toString())
129+
args.toString())
105130
try {
106131
super.exec()
107132
}
108133
catch (Exception e) {
109134
logQuietMessage('\n\033[0;31mrunExample exception!\033[0m Did ' +
110-
'you provide a valid example identifier? E.g. ' +
111-
'\033[0;35m--example="basicoperations.GetCampaigns"\033[0m\n\n' +
112-
e.message)
135+
'you provide a valid example identifier? ' + exampleInvocation +
136+
e.message)
113137
}
114138
}
115139

116-
public String getExampleArguments() {
117-
return exampleArguments
118-
}
119-
140+
// Prints a message to the console, even in quiet mode.
120141
private void logQuietMessage(String message) {
121142
logger.quiet(message)
122143
}
144+
}
145+
146+
class ListExamplesTask extends DefaultTask {
147+
148+
// Regex for extracting descriptions. Isolates the Javadoc comment immediately
149+
// preceeding the class definition.
150+
private static final String COMMENT_REGEX =
151+
'(?s)(/\\*\\*(?:(?!/\\*\\*).)*?\\*/)(?:(?:(?!\\*/).)*?)(?=public class )'
152+
153+
// Regex for comment markers.
154+
private static final String COMMENT_MARKERS = '(/\\*\\*\\s+|\\*\\s|\\*/)'
155+
156+
// The base directory containing all examples.
157+
private static final String MAIN_EXAMPLES_BASE_DIRECTORY =
158+
'src/main/java/com/google/ads/googleads/examples/'
159+
160+
// The base directory containing all examples.
161+
private static final String MIGRATION_EXAMPLES_BASE_DIRECTORY =
162+
'../google-ads-migration-examples/src/main/java/com/google/ads/googleads/migration/'
163+
164+
// The name of the package with the migration examples (currently only one
165+
// such package exists).
166+
private static final String MIGRATION_PACKAGE_NAME = 'campaignmanagement'
167+
168+
// A list of subdirectories to exclude from the printout.
169+
@Input
170+
List<String> directoriesToExclude = ['utils']
171+
172+
// An optional subdirectory; if non-null, only examples in the specified
173+
// subdirectory will be printed.
174+
@Optional
175+
@Input
176+
private String subdirectory = null
177+
178+
// An optional case-sensitive search string; if non-null, only examples whose
179+
// name or description contains the specified term will be printed.
180+
@Optional
181+
@Input
182+
private String searchTerm = null
183+
184+
ListExamplesTask() {
185+
description = 'List all available code examples with package name ' +
186+
'and description.'
187+
}
188+
189+
String getSubdirectory() {
190+
return this.subdirectory
191+
}
192+
193+
List<String> getDirectoriesToExclude() {
194+
return this.directoriesToExclude
195+
}
196+
197+
String getSearchTerm() {
198+
return this.searchTerm
199+
}
200+
201+
@Option(option = 'subdirectory', description = 'Sets a subdirectory of ' +
202+
'google-ads-examples, for example "basicoperations". Only examples in this ' +
203+
'subdirectory will be printed. If not specified, all the examples ' +
204+
'will be printed.')
205+
void setSubdirectory(String subdirectory) {
206+
this.subdirectory = subdirectory.trim()
207+
}
208+
209+
@Option(option = 'searchTerm', description = 'Sets a case-sensitive search term, ' +
210+
'for example "conversion". If set, only examples whose name or description ' +
211+
'contains the specified term will be printed.')
212+
void setSearchTerm(String searchTerm) {
213+
this.searchTerm = searchTerm.trim()
214+
}
215+
216+
// Traverse a given directory, printing the information for each example.
217+
int traverseDirectory(String dir, int offset) {
218+
int count = 0
219+
220+
// Determine the number of leading characters in the directory path. This
221+
// value is different depending on which subproject we are traversing.
222+
int baseLength = (subdirectory ?
223+
project.file(dir).absolutePath.lastIndexOf('/') :
224+
project.file(dir).absolutePath.length()) + 1
225+
226+
project.file(dir).traverse(
227+
type : FileType.FILES,
228+
preDir : { if (it.name in directoriesToExclude)
229+
return FileVisitResult.SKIP_SUBTREE },
230+
excludeNameFilter : { it in directoriesToExclude },
231+
nameFilter : { it.endsWith('.java') },
232+
sort : { a, b -> a.name <=> b.name }
233+
) {
234+
// Strip out the base directory, truncate the .java file extension,
235+
// and replace the / with . to indicate package name.
236+
String className = it.path.substring(baseLength,
237+
it.path.length()-5).replaceAll('/','.')
238+
239+
// Fetch the example description.
240+
Matcher matcher = it.text =~ COMMENT_REGEX
241+
String description =
242+
matcher ? matcher[0][0] : "*description missing*"
243+
244+
if (!searchTerm || className.contains(searchTerm) ||
245+
description.contains(searchTerm)) {
246+
// Print the count, package.class name, and description (without
247+
// comment markers and trailing newlines).
248+
println '' + (++count + offset) + ': ' + className + ' - ' +
249+
description.replaceAll(COMMENT_MARKERS,'').strip()
250+
}
251+
}
252+
253+
return count
254+
}
123255

256+
@TaskAction
257+
void exec() {
258+
String mainExamplesDirectory = subdirectory ?
259+
MAIN_EXAMPLES_BASE_DIRECTORY + subdirectory + '/' : MAIN_EXAMPLES_BASE_DIRECTORY
260+
String migrationExamplesDirectory = subdirectory ?
261+
MIGRATION_EXAMPLES_BASE_DIRECTORY + subdirectory + '/' : MIGRATION_EXAMPLES_BASE_DIRECTORY
262+
int count = 0
263+
264+
count += traverseDirectory(mainExamplesDirectory, count)
265+
266+
// Only print the migration examples if printing all examples or examples
267+
// with the campaignmanagement package name.
268+
if (!subdirectory || subdirectory == MIGRATION_PACKAGE_NAME) {
269+
count += traverseDirectory(migrationExamplesDirectory, count)
270+
}
271+
272+
println "\nTotal examples: $count"
273+
}
124274
}

google-ads-examples/build.gradle

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,43 @@
1616
* This is the build file for the google-ads-examples subproject. It depends on
1717
* the google-ads subproject.
1818
*
19-
* This project contains examples for using the Google Ads API. You can launch
20-
* examples by navigating to the google-ads-examples folder and running the
21-
* provided runExample gradle task. Note that we recommend running with the
22-
* quiet flag (-q) to reduce screen clutter:
23-
* -- Mac/Linux --
24-
* ./gradlew -q runExample --example='basicoperations.GetCampaigns --customerId 1234567890'
19+
* This project contains examples for using the Google Ads API. You can interact
20+
* with the examples by running the provided listExamples and runExample gradle
21+
* tasks. Note that we recommend running with the quiet flag (-q) to reduce
22+
* screen clutter:
23+
* -- Mac/Linux --
24+
* ./gradlew -q runExample --example='basicoperations.GetCampaigns --customerId 1234567890'
25+
* ./gradlew -q listExamples
26+
* ./gradlew -q listExamples --subdirectory="basicoperations"
2527
*
26-
* -- Windows --
27-
* gradlew -q runExample --example='basicoperations.GetCampaigns --customerId 1234567890'
28+
* -- Windows --
29+
* gradlew -q runExample --example='basicoperations.GetCampaigns --customerId 1234567890'
30+
* gradlew -q listExamples
31+
* gradlew -q listExamples --subdirectory="basicoperations"
32+
*
33+
* The listExamples task can also search for a specific term in example names
34+
* and descriptions using the --search argument. Note that these search terms
35+
* are case-sensitive:
36+
* ./gradlew -q listExamples --subdirectory="basicoperations" --searchTerm="REMOVE"
2837
*/
2938

3039
plugins {
31-
id 'com.google.api-ads.java-conventions'
40+
id 'com.google.api-ads.java-conventions'
3241
}
3342

3443
dependencies {
35-
implementation project(':google-ads')
36-
implementation 'org.apache.commons:commons-lang3:3.11'
37-
implementation 'com.beust:jcommander:1.72'
38-
implementation 'joda-time:joda-time:2.8.2'
39-
runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.1'
40-
testImplementation 'org.mockito:mockito-core:2.27.0'
44+
implementation project(':google-ads')
45+
implementation 'org.apache.commons:commons-lang3:3.11'
46+
implementation 'com.beust:jcommander:1.72'
47+
implementation 'joda-time:joda-time:2.8.2'
48+
runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl:2.11.1'
49+
testImplementation 'org.mockito:mockito-core:2.27.0'
4150
}
4251

4352
description = 'Google Ads API client library for Java examples'
4453

4554
task runExample(type: ExampleRunnerTask) {
46-
classpath = sourceSets.main.runtimeClasspath
55+
classpath = sourceSets.main.runtimeClasspath
4756
}
57+
58+
task listExamples(type: ListExamplesTask)

0 commit comments

Comments
 (0)