* adding a plugin that can replace embedme and codesnippet usage in the azure-sdk CI builds
This commit is contained in:
Scott Beddall 2020-05-12 10:31:15 -07:00 коммит произвёл GitHub
Родитель 8e64eb7f0a
Коммит 6417a861e5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 1505 добавлений и 2 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -290,6 +290,7 @@ PublishScripts/
#*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
!packages/java-packages/
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed

Просмотреть файл

@ -7,9 +7,10 @@ This repository contains useful tools that the Azure SDK team utilizes across th
| Package or Intent | Path | Description | Status |
| ------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| doc-warden | [Readme](packages/python-packages/doc-warden/README.md) | A tool used to enforce readme standards across Azure SDK Repos. | [![Build Status](https://dev.azure.com/azure-sdk/public/_apis/build/status/108?branchName=master)](https://dev.azure.com/azure-sdk/public/_build/latest?definitionId=108&branchName=master) |
| pixel-server | [Readme](/web/pixel-server/README.md) | A tiny ASP.NET Core site used to serve a pixel and record impressions. | Not Yet Enabled |
| pixel-server | [Readme](web/pixel-server/README.md) | A tiny ASP.NET Core site used to serve a pixel and record impressions. | Not Yet Enabled |
| pixel insertion tool | [Readme](scripts/python/readme_tracking/readme.md) | A tool used to insert the requests for images served by `pixel server`. | Not Yet Enabled |
| Check Enforcer | [Readme](/tools/check-enforcer/README.md) | Manage GitHub check-runs in a mono-repo. | Not Yet Enabled |
| Check Enforcer | [Readme](tools/check-enforcer/README.md) | Manage GitHub check-runs in a mono-repo. | Not Yet Enabled |
| Maven Plugin for Snippets | [Readme](packages/java-packages/snippet-replacer-maven-plugin/README.md) | A Maven plugin that that updates code snippets referenced from javadoc comments. | Not Yet Enabled |
## Contributing
@ -26,3 +27,5 @@ For more information see the [Code of Conduct FAQ](https://opensource.microsoft.
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
[![Azure DevOps builds](https://img.shields.io/azure-devops/build/azure-sdk/internal/1372?label=eng%2Fcommon%20sync)](https://dev.azure.com/azure-sdk/internal/_build/latest?definitionId=1372&branchName=master)
C:/repo/sdk-tools/packages/java-packages/snippet-replacer-maven-plugin

Просмотреть файл

@ -0,0 +1,133 @@
# Snippet Replacer
`snippet-replacer-maven-plugin` allows java devs to reference actual java code in their javadoc comments. Developers running code written by the Azure team want their documentation to be useful. This plugin helps the Azure SDK team deliver on that promise by ensuring that code samples presented in doc-comments is always _actual running code_.
### How does it work?
First, reference the plugin in your maven project's `pom.xml` file.
```xml
<plugin>
<groupId>com.azure.tools</groupId>
<artifactId>snippet-replacer-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<mode>update</mode>
<targetDir>${project.basedir}</targetDir>
</configuration>
<executions>
<execution>
<phase>process-sources</phase>
<goals>
<goal>snippet-engine</goal>
</goals>
</execution>
</executions>
</plugin>
```
The plugin is intended to be run in the `process-sources` phase.
### The modes of operation
`snippet-replacer` has two modes, `update` and `verify`.
`update` mode runs during your build and _actually updates_ your Javadoc comments with referenced source code.
`verify` mode is intended to be run during CI/PR builds. It will error a javadoc comment that is not updated.
## How to define a referencable snippet
Embed a beginning and end html comment wherever you want the source code inserted.
Within working java code, we have a snippet definition. The string after the `BEGIN:` or `END:` comments is an identifer that can be referenced from javadoc or readmes.
```
public ConfigurationClient createSyncConfigurationClient() {
String connectionString = getConnectionString();
// BEGIN: com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
// END: com.azure.data.applicationconfig.configurationclient.instantiation
return configurationClient;
}
```
The above example defines a code snippet of identifier `com.azure.data.applicationconfig.configurationclient.instantiation`.
### Example of `update`
Within a javadoc comment, a snippet is referenced by a matching pair html comments.
```
...
* <p><strong>Instantiating a synchronous Configuration Client</strong></p>
*
* <!-- src_embed com.azure.core.http.rest.pagedflux.instantiation -->
* <!-- end com.azure.core.http.rest.pagedflux.instantiation -->
*
* <p>View {@link ConfigurationClientBuilder this} for additional ways to construct the client.</p>
*
* @see ConfigurationClientBuilder
*/
@ServiceClient(builder = ConfigurationClientBuilder.class, serviceInterfaces = ConfigurationService.class)
public final class ConfigurationClient {
...
```
After update runs:
```
...
* <p><strong>Instantiating a synchronous Configuration Client</strong></p>
*
* <!-- src_embed com.azure.core.http.rest.pagedflux.instantiation -->
* <pre>
* ConfigurationClient configurationClient = new ConfigurationClientBuilder&#40;&#41;
* .connectionString&#40;connectionString&#41;
* .buildClient&#40;&#41;;
* </pre>
* <!-- end com.azure.core.http.rest.pagedflux.instantiation -->
*
* <p>View {@link ConfigurationClientBuilder this} for additional ways to construct the client.</p>
*
* @see ConfigurationClientBuilder
*/
@ServiceClient(builder = ConfigurationClientBuilder.class, serviceInterfaces = ConfigurationService.class)
public final class ConfigurationClient {
...
```
The referenced code snippet will be embedded (with javadoc appropriate encoding) with the properly spaced code snippet.
### Example of `verify`
This mode is intended for use within CI systems. It will throw an error detailing where snippets are in need of updating. Any dev can rebuild your project with `update` mode and commit the result.
### Functionality against root README.md
While the plugin is mostly intended for use in Javadoc comments, it also will run update and verify operations against the file ${project.basedir}/README.md.
Example reference before update:
````
```Java com.azure.data.applicationconfig.configurationclient.instantiation
```
````
Example of README.md after update:
````
```Java com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
```
````
Which Renders:
```Java com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
```

Просмотреть файл

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.azure.tools</groupId>
<artifactId>snippet-replacer-maven-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>maven-plugin</packaging>
<name>snippet-replacer-maven-plugin Maven Plugin</name>
<url>https://github.com/azure/azure-sdk-tools</url>
<prerequisites>
<maven>${maven.version}</maven>
</prerequisites>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.version>3.3.9</maven.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>${maven.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>${maven.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>3.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>${basedir}/src/test/resources</directory>
</testResource>
</testResources>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_maven-plugin_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<!-- <goalPrefix>maven-archetype-plugin</goalPrefix> -->
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
</configuration>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
<execution>
<id>help-goal</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Просмотреть файл

@ -0,0 +1,33 @@
package azuresdk.plugin;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SnippetDictionary {
private HashMap<String, List<String>> _snippetDictionary = new HashMap<>();
public boolean isActive() {
return !_snippetDictionary.isEmpty();
}
public void beginSnippet(String key) {
if (!this._snippetDictionary.containsKey((key))) {
this._snippetDictionary.put(key, new ArrayList<>());
}
}
public void processLine(String line) {
for (Map.Entry<String, List<String>> entry : this._snippetDictionary.entrySet()) {
entry.getValue().add(line);
}
}
public List<String> finalizeSnippet(String key) {
List<String> value = this._snippetDictionary.get(key);
this._snippetDictionary.remove(key);
return value;
}
}

Просмотреть файл

@ -0,0 +1,15 @@
package azuresdk.plugin;
import java.util.ArrayList;
import java.util.List;
public class SnippetOperationResult<T> {
public T result;
List<VerifyResult> errorList;
public SnippetOperationResult(T resultObject, List<VerifyResult> errors){
super();
this.result = resultObject;
this.errorList = errors;
}
}

Просмотреть файл

@ -0,0 +1,35 @@
package azuresdk.plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@Mojo( name = "snippet-engine", defaultPhase = LifecyclePhase.PROCESS_SOURCES )
public class SnippetPluginEntry
extends AbstractMojo
{
@Parameter( property = "report.mode", required = true )
private String mode;
@Parameter( defaultValue = "${project.basedir}", property = "report.targetDir", required = true )
private File targetDir;
public void execute()
{
try {
SnippetReplacer replacer = new SnippetReplacer(mode, targetDir, getLog());
getLog().info("Completed Execution for Snippet Replacer");
} catch (Exception e) {
getLog().error(e);
return;
}
}
}

Просмотреть файл

@ -0,0 +1,520 @@
package azuresdk.plugin;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.regex.*;
import com.google.common.base.Verify;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
public class SnippetReplacer {
public final static Pattern SNIPPET_DEF_BEGIN = Pattern.compile("\\s*\\/\\/\\s*BEGIN\\:\\s+([a-zA-Z0-9\\.\\#\\-\\_]*)\\s*");
public final static Pattern SNIPPET_DEF_END = Pattern.compile("\\s*\\/\\/\\s*END\\:\\s+([a-zA-Z0-9\\.\\#\\-\\_]*)\\s*");
public final static Pattern SNIPPET_SRC_CALL_BEGIN = Pattern.compile("(\\s*)\\*?\\s*<!--\\s+src_embed\\s+([a-zA-Z0-9\\.\\#\\-\\_]*)\\s*-->");
public final static Pattern SNIPPET_SRC_CALL_END = Pattern.compile("(\\s*)\\*?\\s*<!--\\s+end\\s+([a-zA-Z0-9\\.\\#\\-\\_]*)\\s*-->");
public final static Pattern SNIPPET_README_CALL_BEGIN = Pattern.compile("```(\\s*)?Java\\s+([a-zA-Z0-9\\.\\#\\-\\_]*)\\s*");
public final static Pattern SNIPPET_README_CALL_END = Pattern.compile("```");
public final static Pattern WHITESPACE_EXTRACTION = Pattern.compile("(\\s*)(.*)");
private static PathMatcher SAMPLE_PATH_GLOB = FileSystems.getDefault().getPathMatcher("glob:**/src/samples/java/**/*.java");
private static PathMatcher JAVA_GLOB = FileSystems.getDefault().getPathMatcher("glob:**/*.java");
private static HashMap<String, String> REPLACEMENT_SET = new HashMap<String, String>(){{
put("\"", "&quot;");
put(">", "&gt;");
put("<", "&lt;");
put("@", "{@literal @}");
put("{", "&#123;");
put("}", "&#125;");
put("(", "&#40;");
put(")", "&#41;");
put("/", "&#47;");
put("\\", "&#92;");
}};
public SnippetReplacer(){}
public SnippetReplacer(String mode, File folderToVerify, Log logger) throws MojoExecutionException, IOException {
switch (mode) {
case "update":
this.runUpdate(folderToVerify, logger);
break;
case "verify":
this.runVerification(folderToVerify, logger);
break;
default:
throw new MojoExecutionException(String.format("Unrecognized snippet-replacer mode: %s.", mode));
}
}
/**
* The "verification" operation encapsulated by this function is as follows.
*
* 1. Scan under the target direction for all discovered code snippet DEFINITIONS
* 2. Examine all snippet CALLS, finding where updates are needed.
* 3. Report all discovered snippets in need of update as well as all bad snippet calls
*
* A "bad snippet call" is simply calling for a snippet whose Id has no definition.
*
* See {@link #runUpdate(File, Log)} for details on actually defining and calling snippets.
*/
public void runVerification(File folderToVerify, Log logger) throws IOException, MojoExecutionException {
List<Path> allLocatedJavaFiles = glob(folderToVerify.toPath(), JAVA_GLOB);
List<Path> snippetSources = globFiles(allLocatedJavaFiles, SAMPLE_PATH_GLOB);
List<VerifyResult> snippetsNeedingUpdate = new ArrayList<>();
HashMap<String, List<String>> foundSnippets = new HashMap<>();
List<VerifyResult> badSnippetCalls = new ArrayList<>();
// scan the sample files for all the snippet files
foundSnippets = this.getAllSnippets(snippetSources);
// walk across all the java files, run UpdateSrcSnippets
for (Path sourcePath : allLocatedJavaFiles) {
SnippetOperationResult<List<VerifyResult>> verifyResult = this.verifySrcSnippets(sourcePath, foundSnippets);
snippetsNeedingUpdate.addAll(verifyResult.result);
badSnippetCalls.addAll(verifyResult.errorList);
}
// now find folderToVerify/README.md
// run Update ReadmeSnippets on that
File readmeInBaseDir = new File(folderToVerify, "README.md");
SnippetOperationResult<List<VerifyResult>> rdmeResult = this.verifyReadmeSnippets(readmeInBaseDir.toPath(), foundSnippets);
snippetsNeedingUpdate.addAll(rdmeResult.result);
badSnippetCalls.addAll(rdmeResult.errorList);
if (snippetsNeedingUpdate.size() > 0 || badSnippetCalls.size() > 0) {
for (VerifyResult result : snippetsNeedingUpdate) {
logger.error(String.format("SnippetId %s needs update in file %s.", result.SnippetWithIssues, result.ReadmeLocation.toString()));
}
for (VerifyResult result : badSnippetCalls) {
logger.error(String.format("Unable to locate snippet with Id of %s. Reference in %s", result.SnippetWithIssues, result.ReadmeLocation.toString()));
}
throw new MojoExecutionException("Snippet-Replacer has encountered errors, check above output for details.");
}
}
/**
* This method encapsulates the "update" lifecycle of the snippet-replacer plugin.
*
* Given a root folder, the plugin will scan for snippet DEFINITIONS or snippet CALLS. Once a snippet definition
* index has been formulated, all java files located under the target directory will have snippet CALLS updated with
* the source from the DEFINITIONS.
*
* <p><strong>Snippet Definition</strong></p>
*
* A snippet definition is delineated by BEGIN and END comments directly in your java source.
* Example:
* <pre>
* // BEGIN: com.azure.data.applicationconfig.configurationclient.instantiation
* ConfigurationClient configurationClient = new ConfigurationClientBuilder&#40;&#41;
* .connectionString&#40;connectionString&#41;
* .buildClient&#40;&#41;;
* // END: com.azure.data.applicationconfig.configurationclient.instantiation
* </pre>
*
* <p><strong>Calling a Snippet</strong></p>
*
* From within a javadoc comment, embed an html comment
* &#47;*
* &lt;!-- src_embed com.azure.data.applicationconfig.configurationclient.instantiation --&gt;
* ConfigurationClient configurationClient = new ConfigurationClientBuilder&#40;&#41;
* .connectionString&#40;connectionString&#41;
* .buildClient&#40;&#41;;
* &lt;!-- end com.azure.data.applicationconfig.configurationclient.instantiation --&gt;
* Other javadoc details perhaps.
* *&#47;
* public void myfunction()
* </pre>
*
* After finishing update operations, this function will throw a MojoExecutionException after reporting all snippet
* CALLS that have no DEFINITION.
*/
public void runUpdate(File folderToVerify, Log logger) throws IOException, MojoExecutionException {
List<Path> allLocatedJavaFiles = glob(folderToVerify.toPath(), JAVA_GLOB);
List<Path> snippetSources = globFiles(allLocatedJavaFiles, SAMPLE_PATH_GLOB);
HashMap<String, List<String>> foundSnippets = new HashMap<String, List<String>>();
List<VerifyResult> badSnippetCalls = new ArrayList<>();
// scan the sample files for all the snippet files
foundSnippets = this.getAllSnippets(snippetSources);
// walk across all the java files, run UpdateSrcSnippets
for (Path sourcePath : allLocatedJavaFiles) {
badSnippetCalls.addAll(this.updateSrcSnippets(sourcePath, foundSnippets));
}
// now find folderToVerify/README.md
// run Update ReadmeSnippets on that
File readmeInBaseDir = new File(folderToVerify, "README.md");
badSnippetCalls.addAll(this.updateReadmeSnippets(readmeInBaseDir.toPath(), foundSnippets));
if (badSnippetCalls.size() > 0) {
for (VerifyResult result : badSnippetCalls) {
logger.error(String.format("Unable to locate snippet with Id of %s. Reference in %s", result.SnippetWithIssues, result.ReadmeLocation.toString()));
}
throw new MojoExecutionException("Discovered snippets in need of updating. Please run this plugin in update mode and commit the changes.");
}
}
public List<VerifyResult> updateReadmeSnippets(Path file, HashMap<String, List<String>> snippetMap) throws IOException, MojoExecutionException {
List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
SnippetOperationResult<StringBuilder> opResult = this.updateSnippets(file,
lines,
SNIPPET_README_CALL_BEGIN,
SNIPPET_README_CALL_END,
snippetMap,
"",
"",
0,
"",
true);
if (opResult.result != null) {
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
writer.write(opResult.result.toString());
}
}
return opResult.errorList;
}
public List<VerifyResult> updateSrcSnippets(Path file, HashMap<String, List<String>> snippetMap) throws IOException, MojoExecutionException {
List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
SnippetOperationResult<StringBuilder> opResult = this.updateSnippets(file,
lines,
SNIPPET_SRC_CALL_BEGIN,
SNIPPET_SRC_CALL_END,
snippetMap,
"<pre>",
"</pre>",
1,
"* ",
false);
if (opResult.result != null) {
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
writer.write(opResult.result.toString());
}
}
return opResult.errorList;
}
public SnippetOperationResult<StringBuilder> updateSnippets(Path file, List<String> lines, Pattern beginRegex, Pattern endRegex, HashMap<String, List<String>> snippetMap,
String preFence, String postFence, int prefixGroupNum, String additionalLinePrefix, boolean disableEscape) {
List<VerifyResult> badSnippetCalls = new ArrayList<>();
StringBuilder modifiedLines = new StringBuilder();
boolean inSnippet = false;
boolean needsAmend = false;
String lineSep = System.lineSeparator();
String currentSnippetId = "";
for (String line : lines) {
Matcher begin = beginRegex.matcher(line);
Matcher end = endRegex.matcher(line);
if (begin.matches()) {
modifiedLines.append(line + lineSep);
currentSnippetId = begin.group(2);
inSnippet = true;
}
else if (end.matches()) {
if (inSnippet) {
List<String> newSnippets = null;
if (snippetMap.containsKey(currentSnippetId)) {
newSnippets = snippetMap.get(currentSnippetId);
} else {
badSnippetCalls.add(new VerifyResult(file, currentSnippetId));
needsAmend = true;
inSnippet = false;
continue;
}
List<String> modifiedSnippets = new ArrayList<>();
// We use this additional prefix because in src snippet cases we need to prespace
// for readme snippet cases we DONT need the prespace at all.
String linePrefix = this.prefixFunction(end, prefixGroupNum, additionalLinePrefix);
for (String snippet : this.respaceLines(newSnippets)) {
String moddedSnippet = disableEscape ? snippet : this.escapeString(snippet);
modifiedSnippets.add(moddedSnippet.length() == 0
? linePrefix.replaceAll("[\\s]+$", "") + lineSep
: linePrefix + moddedSnippet + lineSep);
}
if (preFence != null && preFence.length() > 0) {
modifiedLines.append(linePrefix + preFence + lineSep);
}
modifiedLines.append(String.join("", modifiedSnippets));
if (postFence != null && postFence.length() > 0) {
modifiedLines.append(linePrefix + postFence + lineSep);
}
modifiedLines.append(line + lineSep);
needsAmend = true;
inSnippet = false;
}
}
else {
if (inSnippet) {
// do nothing. we'll write everything at the end,
// we'd do a comparison here if we were verifying
}
else {
modifiedLines.append(line + lineSep);
}
}
}
if (needsAmend) {
return new SnippetOperationResult<StringBuilder>(modifiedLines, badSnippetCalls);
}
else{
return null;
}
}
public SnippetOperationResult<List<VerifyResult>> verifyReadmeSnippets(Path file, HashMap<String, List<String>> snippetMap) throws IOException, MojoExecutionException {
List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
return this.verifySnippets(file, lines, SNIPPET_README_CALL_BEGIN, SNIPPET_README_CALL_END, snippetMap, "", "", 0, "", true);
}
public SnippetOperationResult<List<VerifyResult>> verifySrcSnippets(Path file, HashMap<String, List<String>> snippetMap) throws IOException, MojoExecutionException {
List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
return this.verifySnippets(file, lines, SNIPPET_SRC_CALL_BEGIN, SNIPPET_SRC_CALL_END, snippetMap, "<pre>", "</pre>", 1, "* ", false);
}
public SnippetOperationResult<List<VerifyResult>> verifySnippets(Path file, List<String> lines, Pattern beginRegex, Pattern endRegex,
HashMap<String, List<String>> snippetMap, String preFence, String postFence, int prefixGroupNum,
String additionalLinePrefix, boolean disableEscape) {
boolean inSnippet = false;
String lineSep = System.lineSeparator();
List<String> currentSnippetSet = null;
List<VerifyResult> foundIssues = new ArrayList<>();
List<VerifyResult> badSnippetCalls = new ArrayList<>();
String currentSnippetId = "";
for (String line : lines) {
Matcher begin = beginRegex.matcher(line);
Matcher end = endRegex.matcher(line);
if (begin.matches()) {
currentSnippetId = begin.group(2);
inSnippet = true;
currentSnippetSet = new ArrayList<>();
}
else if (end.matches()) {
if (inSnippet) {
List<String> newSnippets;
if (snippetMap.containsKey(currentSnippetId)) {
newSnippets = snippetMap.get(currentSnippetId);
} else {
badSnippetCalls.add(new VerifyResult(file, currentSnippetId));
inSnippet = false;
currentSnippetSet = null;
continue;
}
List<String> modifiedSnippets = new ArrayList<>();
// We use this additional prefix because in src snippet cases we need to prespace
// for readme snippet cases we DONT need the prespace at all.
String linePrefix = this.prefixFunction(end, prefixGroupNum, additionalLinePrefix);
for (String snippet : this.respaceLines(newSnippets)) {
String moddedSnippet = disableEscape ? snippet : this.escapeString(snippet);
modifiedSnippets.add(moddedSnippet.length() == 0
? linePrefix.replaceAll("[\\s]+$", "") + lineSep
: linePrefix + moddedSnippet + lineSep);
}
Collections.sort(modifiedSnippets);
Collections.sort(currentSnippetSet);
if (!modifiedSnippets.equals(currentSnippetSet)) {
foundIssues.add(new VerifyResult(file, currentSnippetId));
}
inSnippet = false;
currentSnippetSet = null;
}
}
else {
if (inSnippet) {
if (preFence.length() > 0 && postFence.length() > 0) {
if (!line.contains(preFence) && !line.contains(postFence)) {
currentSnippetSet.add(line + lineSep);
}
}
else {
currentSnippetSet.add(line + lineSep);
}
}
}
}
return new SnippetOperationResult<List<VerifyResult>>(foundIssues, badSnippetCalls);
}
public HashMap<String, List<String>> getAllSnippets(List<Path> snippetSources) throws IOException, MojoExecutionException {
HashMap<String, List<String>> locatedSnippets = new HashMap<>();
List<VerifyResult> detectedIssues = new ArrayList<>();
for(Path samplePath: snippetSources){
List<String> fileContent = Files.readAllLines(samplePath, StandardCharsets.UTF_8);
HashMap<String, List<String>> tempSnippetMap = new HashMap<>();
SnippetDictionary snippetReader = new SnippetDictionary();
int counter = 0;
for (String line : fileContent) {
Matcher begin = SNIPPET_DEF_BEGIN.matcher(line);
Matcher end = SNIPPET_DEF_END.matcher(line);
if (begin.matches() ){
String id_beginning = begin.group(1);
snippetReader.beginSnippet((id_beginning));
}
else if (end.matches()) {
String id_ending = end.group(1);
List<String> snippetContent = snippetReader.finalizeSnippet((id_ending));
if (!tempSnippetMap.containsKey((id_ending))) {
tempSnippetMap.put(id_ending, snippetContent);
}
else {
// detect duplicate in file
detectedIssues.add(new VerifyResult(samplePath, id_ending));
}
}
else if (snippetReader.isActive()) {
snippetReader.processLine(line);
}
counter++;
}
// we need to examine them individually, as we want to get a complete list of all the duplicates in a run
for (String snippetId : tempSnippetMap.keySet()) {
if (!locatedSnippets.containsKey(snippetId)) {
locatedSnippets.put(snippetId, tempSnippetMap.get(snippetId));
}
else {
// detect duplicate across multiple files
detectedIssues.add(new VerifyResult(samplePath, snippetId));
}
}
};
if (detectedIssues.size() > 0) {
throw new MojoExecutionException("Duplicate Snippet Definitions Detected. " + System.lineSeparator() + this.getErrorString(detectedIssues));
}
return locatedSnippets;
}
private String getErrorString(List<VerifyResult> errors) {
StringBuilder results = new StringBuilder();
for (VerifyResult result : errors) {
results.append(String.format("Duplicate snippetId %s detected in %s.", result.SnippetWithIssues, result.ReadmeLocation.toString()) + System.lineSeparator());
}
return results.toString();
}
private List<String> respaceLines(List<String> snippetText) {
// get List of all the the leading whitespace in the sample
// toss out lines that are empty (as they shouldn't mess with the minimum)
String minWhitespace = null;
List<String> modifiedStrings = new ArrayList<>();
for (String snippetLine : snippetText) {
// only look at non-whitespace only strings for the min indent
if(snippetLine.trim().length() != 0) {
Matcher leadSpaceMatch = WHITESPACE_EXTRACTION.matcher(snippetLine);
if (leadSpaceMatch.matches()) {
String leadSpace = leadSpaceMatch.group(1);
if (minWhitespace == null || leadSpace.length() < minWhitespace.length())
minWhitespace = leadSpace;
}
}
}
for (String snippetLine : snippetText) {
modifiedStrings.add(snippetLine.replaceFirst(minWhitespace, ""));
}
return modifiedStrings;
}
private int getEndIndex(List<String> lines, int startIndex) {
for (int i = startIndex; i < lines.size(); i++) {
Matcher end = SNIPPET_SRC_CALL_END.matcher(lines.get(i));
if (end.matches())
return i;
}
return -1;
}
private String prefixFunction(Matcher match, int groupNum, String additionalPrefix) {
// if we pass -1 as the matcher groupNum, we don't want any prefix at all
if (match == null || groupNum < 1) {
return "";
} else {
return match.group(groupNum) + additionalPrefix;
}
}
private String escapeString(String target) {
if (target != null && target.trim().length() > 0) {
for (String key : this.REPLACEMENT_SET.keySet()) {
target = target.replace(key, REPLACEMENT_SET.get(key));
}
}
return target;
}
private List<Path> glob(Path rootFolder, PathMatcher pathMatcher) throws IOException {
List<Path> locatedPaths = new ArrayList<>();
Files.walkFileTree(rootFolder, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (pathMatcher.matches(file)) {
locatedPaths.add(file);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
return locatedPaths;
}
private List<Path> globFiles(List<Path> paths, PathMatcher pathMatcher) throws IOException {
List<Path> locatedPaths = new ArrayList<>();
for (Path path : paths) {
if (pathMatcher.matches(path)) {
locatedPaths.add(path);
}
};
return locatedPaths;
}
}

Просмотреть файл

@ -0,0 +1,15 @@
package azuresdk.plugin;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
public class VerifyResult {
public String SnippetWithIssues;
public Path ReadmeLocation;
public VerifyResult(Path readmeLocation, String snippetIdWithIssues) {
this.ReadmeLocation = readmeLocation;
this.SnippetWithIssues = snippetIdWithIssues;
}
}

Просмотреть файл

@ -0,0 +1,186 @@
package azuresdk.plugin;
import static org.junit.Assert.*;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.Test;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class SnippetReplacerTests {
private Path _getPathToResource(String relativeLocation){
String pathToTestFile = SnippetReplacerTests.class.getResource(relativeLocation).getPath();
if(pathToTestFile.startsWith("/")){
pathToTestFile = pathToTestFile.substring(1);
}
return Paths.get(pathToTestFile);
}
@Test
public void testSrcParse()
throws Exception
{
Path testFile = _getPathToResource("../../project-to-test/basic_src_snippet_parse.txt");
HashMap<String, List<String>> foundSnippets = new SnippetReplacer().getAllSnippets(
new ArrayList<Path>(Arrays.asList(testFile.toAbsolutePath())));
assertTrue(foundSnippets.size() == 3);
assertTrue(
foundSnippets.get("com.azure.data.applicationconfig.configurationclient.instantiation").size() == 3);
assertTrue(
foundSnippets.get("com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String").size() == 3);
assertTrue(foundSnippets.get("com.azure.core.http.rest.pagedflux.instantiation").size() == 9);
}
@Test
public void testSrcInsertion()
throws Exception
{
/*
Ensures html encoding, empty and populated snippets replacement
*/
Path snippetSourceFile = _getPathToResource("../../project-to-test/basic_src_snippet_parse.txt");
Path codeForReplacement = _getPathToResource("../../project-to-test/basic_src_snippet_insertion_before.txt");
Path expectedOutCome = _getPathToResource("../../project-to-test/basic_src_snippet_insertion_after.txt");
SnippetReplacer testReplacer = new SnippetReplacer();
List<String> testLines = Files.readAllLines(codeForReplacement, StandardCharsets.UTF_8);
byte[] rawBytes = Files.readAllBytes(expectedOutCome);
String expectedString = new String(rawBytes, StandardCharsets.UTF_8);
HashMap<String, List<String>> foundSnippets = testReplacer.getAllSnippets(
new ArrayList<Path>(Arrays.asList(snippetSourceFile.toAbsolutePath())));
SnippetOperationResult<StringBuilder> opResult = testReplacer.updateSnippets(codeForReplacement, testLines,
testReplacer.SNIPPET_SRC_CALL_BEGIN, testReplacer.SNIPPET_SRC_CALL_END, foundSnippets,
"<pre>", "</pre>", 1, "* ", false);
assertTrue(opResult.result != null);
assertEquals(opResult.result.toString(),expectedString);
}
@Test
public void testReadmeInsertion()
throws Exception {
/*
Ensures html encoding, empty and populated snippets replacement
*/
Path snippetSourceFile = _getPathToResource("../../project-to-test/basic_src_snippet_parse.txt");
Path codeForReplacement = _getPathToResource("../../project-to-test/basic_readme_insertion_before.txt");
Path expectedOutCome = _getPathToResource("../../project-to-test/basic_readme_insertion_after.txt");
SnippetReplacer testReplacer = new SnippetReplacer();
List<String> testLines = Files.readAllLines(codeForReplacement, StandardCharsets.UTF_8);
byte[] rawBytes = Files.readAllBytes(expectedOutCome);
String expectedString = new String(rawBytes, StandardCharsets.UTF_8);
HashMap<String, List<String>> foundSnippets = testReplacer.getAllSnippets(
new ArrayList<Path>(Arrays.asList(snippetSourceFile.toAbsolutePath())));
SnippetOperationResult<StringBuilder> opResult = testReplacer.updateSnippets(codeForReplacement, testLines,
testReplacer.SNIPPET_README_CALL_BEGIN, testReplacer.SNIPPET_README_CALL_END, foundSnippets,
"", "", 0, "", true);
assertTrue(opResult.result != null);
assertTrue(opResult.result.toString().equals(expectedString));
}
@Test
public void testReadmeVerification() throws Exception {
Path snippetSourceFile = _getPathToResource("../../project-to-test/basic_src_snippet_parse.txt");
Path verification = _getPathToResource("../../project-to-test/readme_insertion_verification_failure.txt");
SnippetReplacer testReplacer = new SnippetReplacer();
List<String> testLines = Files.readAllLines(verification, StandardCharsets.UTF_8);
HashMap<String, List<String>> foundSnippets = testReplacer.getAllSnippets(
new ArrayList<Path>(Arrays.asList(snippetSourceFile.toAbsolutePath())));
SnippetOperationResult<List<VerifyResult>> opResult = testReplacer.verifySnippets(verification, testLines,
testReplacer.SNIPPET_README_CALL_BEGIN, testReplacer.SNIPPET_README_CALL_END, foundSnippets,
"", "", 0, "", true);
assertTrue(opResult.result != null);
assertTrue(opResult.result.size() == 1);
assertTrue(opResult.result.get(0).SnippetWithIssues.equals("com.azure.core.http.rest.pagedflux.instantiation"));
}
@Test
public void testSrcVerification() throws Exception {
Path snippetSourceFile = _getPathToResource("../../project-to-test/basic_src_snippet_parse.txt");
Path verification = _getPathToResource("../../project-to-test/src_insertion_verification_failure.txt");
SnippetReplacer testReplacer = new SnippetReplacer();
List<String> testLines = Files.readAllLines(verification, StandardCharsets.UTF_8);
HashMap<String, List<String>> foundSnippets = testReplacer.getAllSnippets(
new ArrayList<Path>(Arrays.asList(snippetSourceFile.toAbsolutePath())));
SnippetOperationResult<List<VerifyResult>> opResult = testReplacer.verifySnippets(verification, testLines,
testReplacer.SNIPPET_SRC_CALL_BEGIN, testReplacer.SNIPPET_SRC_CALL_END, foundSnippets,
"<pre>", "</pre>", 1, "* ", false);
assertTrue(opResult.result != null);
assertTrue(opResult.result.size() == 2);
assertTrue(opResult.result.get(0).SnippetWithIssues.equals("com.azure.data.applicationconfig.configurationclient.instantiation"));
assertTrue(opResult.result.get(1).SnippetWithIssues.equals("com.azure.core.http.rest.pagedflux.instantiation"));
}
@Test
public void emptySnippetWorks() throws Exception {
Path single = _getPathToResource("../../project-to-test/empty_snippet_def.txt");
List<Path> srcs = new ArrayList<Path>(Arrays.asList(single.toAbsolutePath()));
HashMap<String, List<String>> foundSnippets = new SnippetReplacer().getAllSnippets(srcs);
assertTrue(foundSnippets.keySet().size() == 1);
assertTrue(foundSnippets.containsKey("com.azure.data.applicationconfig.configurationclient.testEmpty"));
}
@Test
public void duplicateSnippetsCrashWithSingleFile() {
try {
Path single = _getPathToResource("../../project-to-test/duplicate_snippet_src.txt");
List<Path> srcs = new ArrayList<Path>(Arrays.asList(single.toAbsolutePath()));
HashMap<String, List<String>> foundSnippets = new SnippetReplacer().getAllSnippets(srcs);
} catch (Exception e){
// check for snippet id in string
assertTrue(e.toString().contains("com.azure.data.applicationconfig.configurationclient.instantiation"));
}
}
@Test
public void duplicateSnippetsCrashWithMultipleFiles() {
try {
Path single = _getPathToResource("../../project-to-test/duplicate_snippet_src.txt");
Path multiple = _getPathToResource("../../project-to-test/duplicate_snippet_src_multiple.txt");
List<Path> srcs = new ArrayList<Path>(Arrays.asList(single.toAbsolutePath(), multiple.toAbsolutePath()));
HashMap<String, List<String>> foundSnippets = new SnippetReplacer().getAllSnippets(srcs);
} catch (Exception e){
// check for snippet id in string
assertTrue(e.toString().contains("com.azure.data.applicationconfig.configurationclient.instantiation"));
// should be one duplicate message from each file
assertTrue((e.toString().split("Duplicate snippetId", -1).length - 1) == 2);
}
}
@Test
public void notFoundSnippetCrashes() throws IOException{
HashMap<String, List<String>> emptyMap = new HashMap<String, List<String>>();
Path codeForReplacement = _getPathToResource("../../project-to-test/basic_src_snippet_insertion_before.txt");
List<String> testLines = Files.readAllLines(codeForReplacement, StandardCharsets.UTF_8);
SnippetReplacer testReplacer = new SnippetReplacer();
SnippetOperationResult<StringBuilder> opResult = testReplacer.updateSnippets(codeForReplacement, testLines,
testReplacer.SNIPPET_SRC_CALL_BEGIN, testReplacer.SNIPPET_SRC_CALL_END, emptyMap, "<pre>",
"</pre>", 1, "* ", false);
assertTrue(opResult.errorList.size() == 3);
}
}

Просмотреть файл

@ -0,0 +1,23 @@
##### Create a Configuration Client
Once you have the value of the connection string you can create the configuration client:
```Java com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
```
or
``` Java com.azure.core.http.rest.pagedflux.instantiation
// A supplier that fetches the first page of data from source/service
Supplier<Mono<PagedResponse<Integer>>> firstPageRetriever = () -> getFirstPage();
// A function that fetches subsequent pages of data from source/service given a continuation token
Function<String, Mono<PagedResponse<Integer>>> nextPageRetriever =
continuationToken -> getNextPage(continuationToken);
PagedFlux<Integer> pagedFlux = new PagedFlux<>(firstPageRetriever,
nextPageRetriever);
```

Просмотреть файл

@ -0,0 +1,15 @@
##### Create a Configuration Client
Once you have the value of the connection string you can create the configuration client:
```Java com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
Some other crap
```
or
``` Java com.azure.core.http.rest.pagedflux.instantiation
```

Просмотреть файл

@ -0,0 +1,86 @@
import java.time.OffsetDateTime;
/**
* This class provides a client that contains all the operations for {@link ConfigurationSetting ConfigurationSettings}
* in Azure App Configuration Store. Operations allowed by the client are adding, retrieving, deleting, set read-only
* status ConfigurationSettings, and listing settings or revision of a setting based on a
* {@link SettingSelector filter}.
*
* <p><strong>Instantiating a synchronous Configuration Client</strong></p>
*
* <!-- src_embed com.azure.data.applicationconfig.configurationclient.instantiation -->
* <pre>
* ConfigurationClient configurationClient = new ConfigurationClientBuilder&#40;&#41;
* .connectionString&#40;connectionString&#41;
* .buildClient&#40;&#41;;
* </pre>
* <!-- end com.azure.data.applicationconfig.configurationclient.instantiation -->
*
* <p>View {@link ConfigurationClientBuilder this} for additional ways to construct the client.</p>
*
* @see ConfigurationClientBuilder
*/
@ServiceClient(builder = ConfigurationClientBuilder.class, serviceInterfaces = ConfigurationService.class)
public final class ConfigurationClient {
private final ConfigurationAsyncClient client;
/**
* Creates a ConfigurationClient that sends requests to the configuration service at {@code serviceEndpoint}. Each
* service call goes through the {@code pipeline}.
*
* @param client The {@link ConfigurationAsyncClient} that the client routes its request through.
*/
ConfigurationClient(ConfigurationAsyncClient client) {
this.client = client;
}
/**
* Adds a configuration value in the service if that key does not exist. The {@code label} is optional.
*
* <p><strong>Code Samples</strong></p>
*
* <p>Add a setting with the key "prodDBConnection", label "westUS" and value "db_connection".</p>
*
* <!-- src_embed com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String -->
* <pre>
* ConfigurationSetting result = configurationClient
* .addConfigurationSetting&#40;&quot;prodDBConnection&quot;, &quot;westUS&quot;, &quot;db_connection&quot;&#41;;
* System.out.printf&#40;&quot;Key: %s, Label: %s, Value: %s&quot;, result.getKey&#40;&#41;, result.getLabel&#40;&#41;, result.getValue&#40;&#41;&#41;;
* </pre>
* <!-- end com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String -->
*
* @param key The key of the configuration setting to add.
* @param label The label of the configuration setting to create. If {@code null} no label will be used.
* @param value The value associated with this configuration setting key.
* @return The {@link ConfigurationSetting} that was created, or {@code null} if a key collision occurs or the key
* is an invalid value (which will also throw ServiceRequestException described below).
* @throws IllegalArgumentException If {@code key} is {@code null}.
* @throws ResourceModifiedException If a ConfigurationSetting with the same key exists.
* @throws HttpResponseException If {@code key} is an empty string.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public ConfigurationSetting addConfigurationSetting(String key, String label, String value) {
return addConfigurationSettingWithResponse(
new ConfigurationSetting().setKey(key).setLabel(label).setValue(value), Context.NONE).getValue();
}
/**
* <!-- src_embed com.azure.core.http.rest.pagedflux.instantiation -->
* <pre>
* &#47;&#47; A supplier that fetches the first page of data from source&#47;service
* Supplier&lt;Mono&lt;PagedResponse&lt;Integer&gt;&gt;&gt; firstPageRetriever = &#40;&#41; -&gt; getFirstPage&#40;&#41;;
*
* &#47;&#47; A function that fetches subsequent pages of data from source&#47;service given a continuation token
* Function&lt;String, Mono&lt;PagedResponse&lt;Integer&gt;&gt;&gt; nextPageRetriever =
* continuationToken -&gt; getNextPage&#40;continuationToken&#41;;
*
* PagedFlux&lt;Integer&gt; pagedFlux = new PagedFlux&lt;&gt;&#40;firstPageRetriever,
* nextPageRetriever&#41;;
* </pre>
* <!-- end com.azure.core.http.rest.pagedflux.instantiation -->
*/
public ConfigurationSetting addConfigurationSetting(String key, String label, String value) {
return addConfigurationSettingWithResponse(
new ConfigurationSetting().setKey(key).setLabel(label).setValue(value), Context.NONE).getValue();
}
}

Просмотреть файл

@ -0,0 +1,66 @@
import java.time.OffsetDateTime;
/**
* This class provides a client that contains all the operations for {@link ConfigurationSetting ConfigurationSettings}
* in Azure App Configuration Store. Operations allowed by the client are adding, retrieving, deleting, set read-only
* status ConfigurationSettings, and listing settings or revision of a setting based on a
* {@link SettingSelector filter}.
*
* <p><strong>Instantiating a synchronous Configuration Client</strong></p>
*
* <!-- src_embed com.azure.data.applicationconfig.configurationclient.instantiation -->
* Existing content waiting for cleanup.
* <!-- end com.azure.data.applicationconfig.configurationclient.instantiation -->
*
* <p>View {@link ConfigurationClientBuilder this} for additional ways to construct the client.</p>
*
* @see ConfigurationClientBuilder
*/
@ServiceClient(builder = ConfigurationClientBuilder.class, serviceInterfaces = ConfigurationService.class)
public final class ConfigurationClient {
private final ConfigurationAsyncClient client;
/**
* Creates a ConfigurationClient that sends requests to the configuration service at {@code serviceEndpoint}. Each
* service call goes through the {@code pipeline}.
*
* @param client The {@link ConfigurationAsyncClient} that the client routes its request through.
*/
ConfigurationClient(ConfigurationAsyncClient client) {
this.client = client;
}
/**
* Adds a configuration value in the service if that key does not exist. The {@code label} is optional.
*
* <p><strong>Code Samples</strong></p>
*
* <p>Add a setting with the key "prodDBConnection", label "westUS" and value "db_connection".</p>
*
* <!-- src_embed com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String -->
* <!-- end com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String -->
*
* @param key The key of the configuration setting to add.
* @param label The label of the configuration setting to create. If {@code null} no label will be used.
* @param value The value associated with this configuration setting key.
* @return The {@link ConfigurationSetting} that was created, or {@code null} if a key collision occurs or the key
* is an invalid value (which will also throw ServiceRequestException described below).
* @throws IllegalArgumentException If {@code key} is {@code null}.
* @throws ResourceModifiedException If a ConfigurationSetting with the same key exists.
* @throws HttpResponseException If {@code key} is an empty string.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public ConfigurationSetting addConfigurationSetting(String key, String label, String value) {
return addConfigurationSettingWithResponse(
new ConfigurationSetting().setKey(key).setLabel(label).setValue(value), Context.NONE).getValue();
}
/**
* <!-- src_embed com.azure.core.http.rest.pagedflux.instantiation -->
* <!-- end com.azure.core.http.rest.pagedflux.instantiation -->
*/
public ConfigurationSetting addConfigurationSetting(String key, String label, String value) {
return addConfigurationSettingWithResponse(
new ConfigurationSetting().setKey(key).setLabel(label).setValue(value), Context.NONE).getValue();
}
}

Просмотреть файл

@ -0,0 +1,61 @@
/**
* This class contains code samples for generating javadocs through doclets for {@link ConfigurationClient}
*/
public final class ConfigurationClientJavaDocCodeSnippets {
private String key1 = "key1";
private String key2 = "key2";
private String value1 = "val1";
private String value2 = "val2";
/**
* Generates code sample for creating a {@link ConfigurationClient}
*
* @return An instance of {@link ConfigurationClient}
* @throws IllegalStateException If configuration credentials cannot be created.
*/
public ConfigurationClient createAsyncConfigurationClientWithPipeline() {
/**
* Generates code sample for creating a {@link ConfigurationClient}
*
* @return An instance of {@link ConfigurationClient}
* @throws IllegalStateException If configuration credentials cannot be created
*/
public ConfigurationClient createSyncConfigurationClient() {
String connectionString = getConnectionString();
// BEGIN: com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
// END: com.azure.data.applicationconfig.configurationclient.instantiation
return configurationClient;
}
/**
* Generates code sample for using {@link ConfigurationClient#addConfigurationSetting(String, String, String)}
*/
public void addConfigurationSetting() {
ConfigurationClient configurationClient = createSyncConfigurationClient();
// BEGIN: com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String
ConfigurationSetting result = configurationClient
.addConfigurationSetting("prodDBConnection", "westUS", "db_connection");
System.out.printf("Key: %s, Label: %s, Value: %s", result.getKey(), result.getLabel(), result.getValue());
// END: com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String
}
public void encodedHtmlWorks(){
// BEGIN: com.azure.core.http.rest.pagedflux.instantiation
// A supplier that fetches the first page of data from source/service
Supplier<Mono<PagedResponse<Integer>>> firstPageRetriever = () -> getFirstPage();
// A function that fetches subsequent pages of data from source/service given a continuation token
Function<String, Mono<PagedResponse<Integer>>> nextPageRetriever =
continuationToken -> getNextPage(continuationToken);
PagedFlux<Integer> pagedFlux = new PagedFlux<>(firstPageRetriever,
nextPageRetriever);
// END: com.azure.core.http.rest.pagedflux.instantiation
}
}

Просмотреть файл

@ -0,0 +1,19 @@
public ConfigurationClient createSyncConfigurationClient() {
String connectionString = getConnectionString();
// BEGIN: com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
// END: com.azure.data.applicationconfig.configurationclient.instantiation
return configurationClient;
}
public ConfigurationClient createSyncConfigurationClient() {
String connectionString = getConnectionString();
// BEGIN: com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
// END: com.azure.data.applicationconfig.configurationclient.instantiation
return configurationClient;
}

Просмотреть файл

@ -0,0 +1,9 @@
public ConfigurationClient createSyncConfigurationClient() {
String connectionString = getConnectionString();
// BEGIN: com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
// END: com.azure.data.applicationconfig.configurationclient.instantiation
return configurationClient;
}

Просмотреть файл

@ -0,0 +1,6 @@
public ConfigurationClient createSyncConfigurationClient() {
String connectionString = getConnectionString();
// BEGIN: com.azure.data.applicationconfig.configurationclient.testEmpty
// END: com.azure.data.applicationconfig.configurationclient.testEmpty
return configurationClient;
}

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>azuresdk.plugin</groupId>
<artifactId>snippet-replacer-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Test MyMojo</name>
<build>
<plugins>
<plugin>
<artifactId>maven-my-plugin</artifactId>
<configuration>
<!-- Specify the MyMojo parameter -->
<outputDirectory>target/test-harness/project-to-test</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
</project>

Просмотреть файл

@ -0,0 +1,20 @@
##### Create a Configuration Client
Once you have the value of the connection string you can create the configuration client:
```Java com.azure.data.applicationconfig.configurationclient.instantiation
ConfigurationClient configurationClient = new ConfigurationClientBuilder()
.connectionString(connectionString)
.buildClient();
```
or
``` Java com.azure.core.http.rest.pagedflux.instantiation
Supplier<Mono<PagedResponse<Integer>>> firstPageRetriever = () -> getFirstPage();
// A function that fetches subsequent pages of data from source/service given a continuation token
Function<String, Mono<PagedResponse<Integer>>> nextPageRetriever =
continuationToken -> getNextPage(continuationToken);
```

Просмотреть файл

@ -0,0 +1,84 @@
import java.time.OffsetDateTime;
/**
* This class provides a client that contains all the operations for {@link ConfigurationSetting ConfigurationSettings}
* in Azure App Configuration Store. Operations allowed by the client are adding, retrieving, deleting, set read-only
* status ConfigurationSettings, and listing settings or revision of a setting based on a
* {@link SettingSelector filter}.
*
* <p><strong>Instantiating a synchronous Configuration Client</strong></p>
*
* <!-- src_embed com.azure.data.applicationconfig.configurationclient.instantiation -->
* <pre>
* ConfigurationClient configurationClient = new ConfigurationClientBuilder&#40;&#41;
* .connectionString&#40;connectionString&#41;
* .buildClient&#40;&#41;
* </pre>
* <!-- end com.azure.data.applicationconfig.configurationclient.instantiation -->
*
* <p>View {@link ConfigurationClientBuilder this} for additional ways to construct the client.</p>
*
* @see ConfigurationClientBuilder
*/
@ServiceClient(builder = ConfigurationClientBuilder.class, serviceInterfaces = ConfigurationService.class)
public final class ConfigurationClient {
private final ConfigurationAsyncClient client;
/**
* Creates a ConfigurationClient that sends requests to the configuration service at {@code serviceEndpoint}. Each
* service call goes through the {@code pipeline}.
*
* @param client The {@link ConfigurationAsyncClient} that the client routes its request through.
*/
ConfigurationClient(ConfigurationAsyncClient client) {
this.client = client;
}
/**
* Adds a configuration value in the service if that key does not exist. The {@code label} is optional.
*
* <p><strong>Code Samples</strong></p>
*
* <p>Add a setting with the key "prodDBConnection", label "westUS" and value "db_connection".</p>
*
* <!-- src_embed com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String -->
* <pre>
* ConfigurationSetting result = configurationClient
* .addConfigurationSetting&#40;&quot;prodDBConnection&quot;, &quot;westUS&quot;, &quot;db_connection&quot;&#41;;
* System.out.printf&#40;&quot;Key: %s, Label: %s, Value: %s&quot;, result.getKey&#40;&#41;, result.getLabel&#40;&#41;, result.getValue&#40;&#41;&#41;;
* </pre>
* <!-- end com.azure.data.appconfiguration.ConfigurationClient.addConfigurationSetting#String-String-String -->
*
* @param key The key of the configuration setting to add.
* @param label The label of the configuration setting to create. If {@code null} no label will be used.
* @param value The value associated with this configuration setting key.
* @return The {@link ConfigurationSetting} that was created, or {@code null} if a key collision occurs or the key
* is an invalid value (which will also throw ServiceRequestException described below).
* @throws IllegalArgumentException If {@code key} is {@code null}.
* @throws ResourceModifiedException If a ConfigurationSetting with the same key exists.
* @throws HttpResponseException If {@code key} is an empty string.
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public ConfigurationSetting addConfigurationSetting(String key, String label, String value) {
return addConfigurationSettingWithResponse(
new ConfigurationSetting().setKey(key).setLabel(label).setValue(value), Context.NONE).getValue();
}
/**
* <!-- src_embed com.azure.core.http.rest.pagedflux.instantiation -->
* <pre>
* &#47;&#47; A supplier that fetches the first page of data from source&#47;service
* Supplier&lt;Mono&lt;PagedResponse&lt;Integer&gt;&gt;&gt; firstPageRetriever = &#40;&#41; -&gt; getFirstPage&#40;&#41;;
*
* &#47;&#47; A function that fetches subsequent pages of data from source&#47;service given a continuation token
* Function&lt;String, Mono&lt;PagedResponse&lt;Integer&gt;&gt;&gt; nextPageRetriever =
* continuationToken -&gt; getNextPage&#40;continuationToken&#41;;
*
* </pre>
* <!-- end com.azure.core.http.rest.pagedflux.instantiation -->
*/
public ConfigurationSetting addConfigurationSetting(String key, String label, String value) {
return addConfigurationSettingWithResponse(
new ConfigurationSetting().setKey(key).setLabel(label).setValue(value), Context.NONE).getValue();
}
}