Skip to content

Commit 3d54c0f

Browse files
authored
Add GCS sink documentation (#561)
1 parent 41e35db commit 3d54c0f

File tree

25 files changed

+570
-607
lines changed

25 files changed

+570
-607
lines changed

.github/workflows/build_site.yml

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,39 +29,12 @@ jobs:
2929
- name: Checkout
3030
uses: actions/checkout@v4
3131

32-
- name: Check for S3SourceConfig
33-
run: find . -name S3SourceConfig.\*
34-
35-
- name: Build site tools
36-
run: ./gradlew :site:build
37-
38-
- name: Check for S3SourceConfig
39-
run: find . -name S3SourceConfig.\*
40-
41-
- name: copy site assets
42-
run: ./gradlew :copySiteAssets
43-
44-
- name: Check for S3SourceConfig
45-
run: find . -name S3SourceConfig.\*
46-
47-
- name: create site
48-
run: ./gradlew createSite
49-
50-
- name: Check for S3SourceConfig
51-
run: find . -name S3SourceConfig.\*
52-
53-
- name: generate javadoc
32+
- name: generate site framework
5433
run: ./gradlew :site:build :copySiteAssets createSite javadoc
5534

56-
- name: Check for S3SourceConfig
57-
run: find . -name S3SourceConfig.\*
58-
5935
- name: Populate the site
6036
run: ./gradlew populateSite
6137

62-
- name: Check for S3SourceConfig
63-
run: find . -name S3SourceConfig.\*
64-
6538
- name: Upload artifact
6639
uses: actions/upload-pages-artifact@v3
6740
with:

azure-sink-connector/src/main/java/io/aiven/kafka/connect/azure/sink/AzureBlobSinkConfigDef.java

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,73 +17,19 @@
1717
package io.aiven.kafka.connect.azure.sink;
1818

1919
import java.util.Map;
20-
import java.util.regex.Pattern;
2120

22-
import org.apache.kafka.common.config.ConfigDef;
23-
import org.apache.kafka.common.config.ConfigException;
2421
import org.apache.kafka.common.config.ConfigValue;
2522

2623
import io.aiven.kafka.connect.common.config.CompressionType;
27-
import io.aiven.kafka.connect.common.config.FileNameFragment;
2824
import io.aiven.kafka.connect.common.config.FragmentDataAccess;
2925
import io.aiven.kafka.connect.common.config.OutputFieldType;
3026
import io.aiven.kafka.connect.common.config.SinkCommonConfig;
3127

3228
public final class AzureBlobSinkConfigDef extends SinkCommonConfig.SinkCommonConfigDef {
3329

34-
private final static Pattern CONTAINER_NAME_PATTERN = Pattern.compile("[0-9a-z][0-9a-z\\-]+[0-9a-z]");
35-
36-
/**
37-
* From Azure documentation:
38-
* <ul>
39-
* <li>Container names must start or end with a letter or number, and can contain only letters, numbers, and the
40-
* hyphen/minus (-) character.</li>
41-
* <li>Every hyphen/minus (-) character must be immediately preceded and followed by a letter or number; consecutive
42-
* hyphens aren't permitted in container names.</li>
43-
* <li>All letters in a container name must be lowercase.</li>
44-
* <li>Container names must be from 3 through 63 characters long.</li>
45-
* </ul>
46-
*/
47-
public static final ConfigDef.Validator CONTAINER_NAME_VALIDATOR = ConfigDef.CompositeValidator
48-
.of(ConfigDef.LambdaValidator.with((name, value) -> {
49-
final int len = value == null ? 0 : value.toString().length();
50-
if (len < 3 || len > 63) {
51-
throw new ConfigException(name, value, "names must be from 3 through 63 characters long.");
52-
}
53-
}, () -> "must be from 3 through 63 characters long"), ConfigDef.LambdaValidator.with((name, value) -> {
54-
if (value.toString().contains("--")) {
55-
throw new ConfigException(name, value,
56-
"Every hyphen/minus (-) character must be immediately preceded and followed by a letter or number; consecutive hyphens aren't permitted in container names.");
57-
}
58-
}, () -> "consecutive hyphens aren't permitted in container names"),
59-
// regex last for speed
60-
ConfigDef.LambdaValidator.with((name, value) -> {
61-
if (!CONTAINER_NAME_PATTERN.matcher(value.toString()).matches()) {
62-
throw new ConfigException(name, value,
63-
"must start or end with a letter or number, and can contain only lower case letters, numbers, and the hyphen/minus (-) character.");
64-
}
65-
}, () -> "start or end with a letter or number, and can contain only lower case letters, numbers, and the hyphen/minus (-) character"));
66-
6730
AzureBlobSinkConfigDef() {
6831
super(OutputFieldType.VALUE, CompressionType.NONE);
6932
AzureBlobConfigFragment.update(this, true);
70-
addFileConfigGroup(this);
71-
}
72-
73-
static void addFileConfigGroup(final ConfigDef configDef) {
74-
75-
configDef.define(AzureBlobSinkConfig.FILE_NAME_PREFIX_CONFIG, ConfigDef.Type.STRING, "",
76-
ConfigDef.LambdaValidator.with((name, value) -> {
77-
assert value instanceof String;
78-
final String valueStr = (String) value;
79-
if (valueStr.length() > 1024) { // NOPMD avoid literal
80-
throw new ConfigException(AzureBlobSinkConfig.AZURE_STORAGE_CONTAINER_NAME_CONFIG, value,
81-
"cannot be longer than 1024 characters");
82-
}
83-
}, () -> ""), ConfigDef.Importance.MEDIUM,
84-
"The prefix to be added to the name of each file put on Azure Blob.", FileNameFragment.GROUP_NAME, 10,
85-
ConfigDef.Width.NONE, AzureBlobSinkConfig.FILE_NAME_PREFIX_CONFIG);
86-
8733
}
8834

8935
@Override

azure-source-connector/build.gradle.kts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,7 @@ tasks.register<JavaExec>("buildConfigMd") {
255255
.plus(sourceSets.main.get().runtimeClasspath)
256256
args =
257257
listOf(
258-
"io.aiven.kafka.connect.azure.source.config.AzureBlobSourceConfig",
259-
"configDef",
258+
"io.aiven.kafka.connect.azure.source.config.AzureBlobSourceConfigDef",
260259
"src/templates/configData.md.vm",
261260
"build/site/markdown/azure-source-connector/AzureBlobSourceConfig.md")
262261
}
@@ -271,8 +270,7 @@ tasks.register<JavaExec>("buildConfigYml") {
271270
.plus(sourceSets.main.get().runtimeClasspath)
272271
args =
273272
listOf(
274-
"io.aiven.kafka.connect.azure.source.config.AzureBlobSourceConfig",
275-
"configDef",
273+
"io.aiven.kafka.connect.azure.source.config.AzureBlobSourceConfigDef",
276274
"src/templates/configData.yml.vm",
277275
"build/site/azure-source-connector/AzureBlobSourceConfig.yml")
278276
}

commons/src/main/java/io/aiven/commons/collections/TimeScale.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public static TimeScale scaleOf(final long milliseconds) {
141141
* @return the String representation.
142142
* @see #scaleOf(long)
143143
*/
144-
public static String size(final int milliseconds) {
144+
public static String size(final long milliseconds) {
145145
return scaleOf(milliseconds).format(milliseconds);
146146
}
147147

commons/src/main/java/io/aiven/kafka/connect/common/config/ConfigFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ protected static String validationMessage(final String name, final Object value,
7474
* @param message
7575
* the message for the error.
7676
*/
77-
protected void registerIssue(final Map<String, ConfigValue> configMap, final String name, final Object value,
77+
public static void registerIssue(final Map<String, ConfigValue> configMap, final String name, final Object value,
7878
final String message) {
7979
configMap.get(name).addErrorMessage(validationMessage(name, value, message));
8080
}

commons/src/main/java/io/aiven/kafka/connect/common/config/FileNameFragment.java

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@
4949
* Fragment to handle all file name extraction operations.
5050
*/
5151
public final class FileNameFragment extends ConfigFragment {
52+
/**
53+
* Flag to support Prefix Template as opposed to a prefix string. TODO To be removed when all implementations
54+
* support the prefix template.
55+
*/
56+
public enum PrefixTemplateSupport {
57+
TRUE, FALSE
58+
}
5259
/**
5360
* The name of the group that this fragment places items in.
5461
*/
@@ -67,6 +74,8 @@ public final class FileNameFragment extends ConfigFragment {
6774
static final String DEFAULT_FILENAME_TEMPLATE = "{{topic}}-{{partition}}-{{start_offset}}";
6875
@VisibleForTesting
6976
public static final String FILE_PATH_PREFIX_TEMPLATE_CONFIG = "file.prefix.template";
77+
@VisibleForTesting
78+
public static final String FILE_NAME_PREFIX_CONFIG = "file.name.prefix";
7079

7180
public static final ConfigDef.Validator COMPRESSION_TYPE_VALIDATOR = new PredicateGatedValidator(Objects::nonNull,
7281
ConfigDef.CaseInsensitiveValidString.in(CompressionType.names().toArray(new String[0])));
@@ -77,7 +86,7 @@ public final class FileNameFragment extends ConfigFragment {
7786
.collect(Collectors.toList())
7887
.toArray(new String[0]));
7988

80-
public static final ConfigDef.Validator TEMPLATE_VALIDATOR = ConfigDef.LambdaValidator.with((name, value) -> {
89+
public static final ConfigDef.Validator PREFIX_VALIDATOR = ConfigDef.LambdaValidator.with((name, value) -> {
8190
if (value == null) {
8291
return;
8392
}
@@ -90,18 +99,24 @@ public final class FileNameFragment extends ConfigFragment {
9099
} catch (IllegalArgumentException e) {
91100
throw new ConfigException(name, value, e.getMessage());
92101
}
93-
}, () -> "See documentation for proper template construction.");
102+
}, () -> "may not start with '.well-known/acme-challenge'");
94103

95104
/**
96105
* The flag that indicates this fragment is being used as for a sink connector.
97106
*/
98107
private final boolean isSink;
99108

109+
/** Map of template variable name to the template variable definition */
100110
private static final Map<String, FilenameTemplateVariable> FILENAME_VARIABLES = new TreeMap<>();
111+
private static final String TEMPLATE_GROUPINGS;
101112

102113
static {
103114
Arrays.stream(FilenameTemplateVariable.values())
104115
.forEach(variable -> FILENAME_VARIABLES.put(variable.name, variable));
116+
TEMPLATE_GROUPINGS = "[" + RecordGrouperFactory.getSupportedVariableGroups()
117+
.stream()
118+
.map(strings -> FilePatternUtils.asPatterns(strings, ", "))
119+
.collect(Collectors.joining("] \n [")) + "]";
105120
}
106121

107122
/**
@@ -253,51 +268,44 @@ private void validateSink(final Map<String, ConfigValue> configMap, final Templa
253268
}
254269
}
255270

256-
/**
257-
* Adds the FileName properties to the configuration definition.
258-
*
259-
* @param configDef
260-
* the configuration definition to update.
261-
* @return the updated configuration definition.
262-
*/
263-
public static int update(final ConfigDef configDef) {
264-
return update(configDef, CompressionType.NONE);
265-
}
266-
267271
/**
268272
* Adds the FileName properties to the configuration definition.
269273
*
270274
* @param configDef
271275
* the configuration definition to update.
272276
* @param defaultCompressionType
273277
* The default compression type. May be {@code null}.
274-
* @return number of items in the file group..
278+
* @return number of items in the file group.
275279
*/
276-
public static int update(final ConfigDef configDef, final CompressionType defaultCompressionType) {
280+
public static int update(final ConfigDef configDef, final CompressionType defaultCompressionType,
281+
final PrefixTemplateSupport prefixTemplateSupport) {
277282
int fileGroupCounter = 0;
278283

279-
configDef.define(FILE_NAME_TEMPLATE_CONFIG, ConfigDef.Type.STRING, null, TEMPLATE_VALIDATOR,
284+
configDef.define(FILE_NAME_TEMPLATE_CONFIG, ConfigDef.Type.STRING, null, PREFIX_VALIDATOR,
280285
ConfigDef.Importance.MEDIUM,
281286
"The template for file names on storage system. "
282287
+ "Supports `{{ variable }}` placeholders for substituting variables. "
283-
+ "Currently supported variables are `topic`, `partition`, and `start_offset` "
284-
+ "(the offset of the first record in the file). "
285-
+ "Only some combinations of variables are valid, which currently are:\n"
286-
+ "- `topic`, `partition`, `start_offset`."
287-
+ "There is also `key` only variable {{key}} for grouping by keys",
288+
+ "Currently supported variables are "
289+
+ String.join(", ", FilePatternUtils.asPatterns(FILENAME_VARIABLES.keySet(), ", "))
290+
+ ". Only some combinations of variables are valid, which currently are: " + TEMPLATE_GROUPINGS,
288291
GROUP_NAME, ++fileGroupCounter, ConfigDef.Width.LONG, FILE_NAME_TEMPLATE_CONFIG);
289292

290-
configDef.define(FILE_PATH_PREFIX_TEMPLATE_CONFIG, ConfigDef.Type.STRING, null, TEMPLATE_VALIDATOR,
291-
ConfigDef.Importance.MEDIUM,
292-
"The template for file names prefixes on storage system. "
293-
+ "Supports `{{ variable }}` placeholders for substituting variables. "
294-
+ "Currently supported variables are `topic`, `partition`, and `start_offset` "
295-
+ "(the offset of the first record in the file). "
296-
+ "Only some combinations of variables are valid, which currently are:\n"
297-
+ "- `topic`, `partition`, `start_offset`."
298-
+ "There is also `key` only variable {{key}} for grouping by keys",
299-
GROUP_NAME, ++fileGroupCounter, ConfigDef.Width.LONG, FILE_PATH_PREFIX_TEMPLATE_CONFIG);
300-
293+
if (prefixTemplateSupport.equals(PrefixTemplateSupport.TRUE)) {
294+
configDef.define(FILE_PATH_PREFIX_TEMPLATE_CONFIG, ConfigDef.Type.STRING, null, PREFIX_VALIDATOR,
295+
ConfigDef.Importance.MEDIUM,
296+
"The template for file names prefixes on storage system. "
297+
+ "Supports `{{ variable }}` placeholders for substituting variables. "
298+
+ "Currently supported variables are `topic`, `partition`, and `start_offset` "
299+
+ "(the offset of the first record in the file). "
300+
+ "Only some combinations of variables are valid, which currently are:\n"
301+
+ "- `topic`, `partition`, `start_offset`."
302+
+ "There is also `key` only variable {{key}} for grouping by keys",
303+
GROUP_NAME, ++fileGroupCounter, ConfigDef.Width.LONG, FILE_PATH_PREFIX_TEMPLATE_CONFIG);
304+
} else {
305+
configDef.define(FILE_NAME_PREFIX_CONFIG, ConfigDef.Type.STRING, "", PREFIX_VALIDATOR,
306+
ConfigDef.Importance.MEDIUM, "The prefix to be added to the name of each file.",
307+
FileNameFragment.GROUP_NAME, ++fileGroupCounter, ConfigDef.Width.LONG, FILE_NAME_PREFIX_CONFIG);
308+
}
301309
configDef.define(FILE_COMPRESSION_TYPE_CONFIG, ConfigDef.Type.STRING, defaultCompressionType.name(),
302310
COMPRESSION_TYPE_VALIDATOR, ConfigDef.Importance.MEDIUM, "The compression type used for files.",
303311
GROUP_NAME, ++fileGroupCounter, ConfigDef.Width.NONE, FILE_COMPRESSION_TYPE_CONFIG,
@@ -394,6 +402,9 @@ public String getPrefixTemplate() {
394402
return getString(FILE_PATH_PREFIX_TEMPLATE_CONFIG);
395403
}
396404

405+
public String getPrefix() {
406+
return getString(FILE_NAME_PREFIX_CONFIG);
407+
}
397408
public static void replaceYyyyUppercase(final String name, final Map<String, String> properties) {
398409
String template = properties.get(name);
399410
if (template != null) {
@@ -522,5 +533,16 @@ public Setter template(final String template) {
522533
public Setter prefixTemplate(final String prefixTemplate) {
523534
return setValue(FILE_PATH_PREFIX_TEMPLATE_CONFIG, prefixTemplate);
524535
}
536+
537+
/**
538+
* Sets the file name prefix template.
539+
*
540+
* @param prefix
541+
* the prefix to use.
542+
* @return this
543+
*/
544+
public Setter prefix(final String prefix) {
545+
return setValue(FILE_NAME_PREFIX_CONFIG, prefix);
546+
}
525547
}
526548
}

commons/src/main/java/io/aiven/kafka/connect/common/config/SinkCommonConfig.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,7 @@ public static class SinkCommonConfigDef extends CommonConfigDef {
176176
public SinkCommonConfigDef(final OutputFieldType defaultFieldType, final CompressionType compressionType) {
177177
super();
178178
OutputFormatFragment.update(this, defaultFieldType);
179-
FileNameFragment.update(this, compressionType);
180-
// not supported at this time.
181-
configKeys().remove(FileNameFragment.FILE_PATH_PREFIX_TEMPLATE_CONFIG);
179+
FileNameFragment.update(this, compressionType, FileNameFragment.PrefixTemplateSupport.FALSE);
182180
}
183181

184182
@Override

commons/src/main/java/io/aiven/kafka/connect/common/config/SourceCommonConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public SourceCommonConfigDef() {
179179
super();
180180
TransformerFragment.update(this);
181181
SourceConfigFragment.update(this);
182-
FileNameFragment.update(this);
182+
FileNameFragment.update(this, CompressionType.NONE, FileNameFragment.PrefixTemplateSupport.TRUE);
183183
}
184184

185185
@Override

commons/src/main/java/io/aiven/kafka/connect/common/grouper/RecordGrouperFactory.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,19 @@ public final class RecordGrouperFactory {
119119
.map(v -> v.stream().map(Pair::getLeft).collect(Collectors.joining(",")))
120120
.collect(Collectors.joining("; "));
121121

122+
/**
123+
* Gets a list of the supported variable groupings. Each list within the list is a grouping of variables that is
124+
* supported.
125+
*
126+
* @return a list of supported variable groupings.
127+
*/
128+
public static List<List<String>> getSupportedVariableGroups() {
129+
return SUPPORTED_VARIABLES.values()
130+
.stream()
131+
.map(lstPair -> lstPair.stream().map(Pair::getLeft).collect(Collectors.toList()))
132+
.collect(Collectors.toList());
133+
}
134+
122135
private RecordGrouperFactory() {
123136
}
124137

0 commit comments

Comments
 (0)