Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,17 @@ private static boolean existingIndex(Collection<TestConfigs> existing, DataType

/** This test generates documentation for the supported output types of the lookup join. */
public void testOutputSupportedTypes() throws Exception {
Map<List<DataType>, DataType> signatures = new LinkedHashMap<>();
Map<List<DocsV3Support.Param>, DataType> signatures = new LinkedHashMap<>();
Copy link
Member Author

Choose a reason for hiding this comment

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

All of these s/DataType/DocsV3Support.Param/ exist so I can plumb FunctionAppliesTo information through the test case instead of an annotation. That's because we generate the type signatures entirely from test cases - no annotation to scan.

for (TestConfigs configs : testConfigurations.values()) {
if (configs.group.equals("unsupported") || configs.group.equals("union-types")) {
continue;
}
for (TestConfig config : configs.configs.values()) {
if (config instanceof TestConfigPasses) {
signatures.put(List.of(config.mainType(), config.lookupType()), null);
signatures.put(
List.of(new DocsV3Support.Param(config.mainType(), List.of()), new DocsV3Support.Param(config.lookupType(), null)),
null
);
}
}
}
Expand Down Expand Up @@ -767,7 +770,7 @@ private boolean isValidDataType(DataType dataType) {
return UNDER_CONSTRUCTION.get(dataType) == null || UNDER_CONSTRUCTION.get(dataType).isEnabled();
}

private static void saveJoinTypes(Supplier<Map<List<DataType>, DataType>> signatures) throws Exception {
private static void saveJoinTypes(Supplier<Map<List<DocsV3Support.Param>, DataType>> signatures) throws Exception {
if (System.getProperty("generateDocs") == null) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -758,10 +758,10 @@ public static void testFunctionInfo() {
for (int i = 0; i < args.size(); i++) {
typesFromSignature.add(new HashSet<>());
}
for (Map.Entry<List<DataType>, DataType> entry : signatures(testClass).entrySet()) {
List<DataType> types = entry.getKey();
for (Map.Entry<List<DocsV3Support.Param>, DataType> entry : signatures(testClass).entrySet()) {
List<DocsV3Support.Param> types = entry.getKey();
for (int i = 0; i < args.size() && i < types.size(); i++) {
typesFromSignature.get(i).add(types.get(i).esNameIfPossible());
typesFromSignature.get(i).add(types.get(i).dataType().esNameIfPossible());
}
if (DataType.UNDER_CONSTRUCTION.containsKey(entry.getValue()) == false) {
returnFromSignature.add(entry.getValue().esNameIfPossible());
Expand Down Expand Up @@ -840,14 +840,14 @@ public static void testFunctionLicenseChecks() throws Exception {
// Go through all signatures and assert that the license is as expected
signatures(testClass).forEach((signature, returnType) -> {
try {
License.OperationMode license = licenseChecker.invoke(signature);
License.OperationMode license = licenseChecker.invoke(signature.stream().map(DocsV3Support.Param::dataType).toList());
assertNotNull("License should not be null", license);

// Construct an instance of the class and then call it's licenseCheck method, and compare the results
Object[] args = new Object[ctor.getParameterCount()];
args[0] = Source.EMPTY;
for (int i = 0; i < signature.size(); i++) {
args[i + 1] = new Literal(Source.EMPTY, null, signature.get(i));
args[i + 1] = new Literal(Source.EMPTY, null, signature.get(i).dataType());
}
Object instance = ctor.newInstance(args);
// Check that object implements the LicenseAware interface
Expand All @@ -874,7 +874,7 @@ private static class TestCheckLicense {

private void assertLicenseCheck(
LicenseAware licenseAware,
List<DataType> signature,
List<DocsV3Support.Param> signature,
boolean allowsBasic,
boolean allowsPlatinum,
boolean allowsEnterprise
Expand Down Expand Up @@ -933,9 +933,9 @@ protected final void assertTypeResolutionFailure(Expression expression) {
/**
* Unique signatures in this test’s parameters.
*/
private static Map<List<DataType>, DataType> signatures;
private static Map<List<DocsV3Support.Param>, DataType> signatures;

public static Map<List<DataType>, DataType> signatures(Class<?> testClass) {
public static Map<List<DocsV3Support.Param>, DataType> signatures(Class<?> testClass) {
if (signatures != null && classGeneratingSignatures == testClass) {
return signatures;
}
Expand All @@ -959,17 +959,17 @@ public static Map<List<DataType>, DataType> signatures(Class<?> testClass) {
if (tc.getData().stream().anyMatch(t -> t.type() == DataType.NULL)) {
continue;
}
List<DataType> types = tc.getData().stream().map(TestCaseSupplier.TypedData::type).toList();
signatures.putIfAbsent(signatureTypes(testClass, types), tc.expectedType());
List<DocsV3Support.Param> sig = tc.getData().stream().map(d -> new DocsV3Support.Param(d.type(), d.appliesTo())).toList();
signatures.putIfAbsent(signatureTypes(testClass, sig), tc.expectedType());
}
return signatures;
}

@SuppressWarnings("unchecked")
private static List<DataType> signatureTypes(Class<?> testClass, List<DataType> types) {
private static List<DocsV3Support.Param> signatureTypes(Class<?> testClass, List<DocsV3Support.Param> types) {
try {
Method method = testClass.getMethod("signatureTypes", List.class);
return (List<DataType>) method.invoke(null, types);
return (List<DocsV3Support.Param>) method.invoke(null, types);
} catch (NoSuchMethodException ingored) {
return types;
} catch (Exception e) {
Expand Down Expand Up @@ -1053,9 +1053,9 @@ private static boolean isAggregation() {
/**
* Should this particular signature be hidden from the docs even though we test it?
*/
static boolean shouldHideSignature(List<DataType> argTypes, DataType returnType) {
static boolean shouldHideSignature(List<DocsV3Support.Param> argTypes, DataType returnType) {
for (DataType dt : DataType.UNDER_CONSTRUCTION.keySet()) {
if (returnType == dt || argTypes.contains(dt)) {
if (returnType == dt || argTypes.stream().anyMatch(p -> p.dataType() == dt)) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
* and partially re-written to satisfy the above requirements.
*/
public abstract class DocsV3Support {
public record Param(DataType dataType, List<FunctionAppliesTo> appliesTo) {}

private static final Logger logger = LogManager.getLogger(DocsV3Support.class);

private static final String DOCS_WARNING_JSON =
Expand Down Expand Up @@ -372,15 +374,15 @@ public License.OperationMode invoke(List<DataType> fieldTypes) throws Exception
protected final String category;
protected final String name;
protected final FunctionDefinition definition;
protected final Supplier<Map<List<DataType>, DataType>> signatures;
protected final Supplier<Map<List<Param>, DataType>> signatures;
protected final Callbacks callbacks;
private final LicenseRequirementChecker licenseChecker;

protected DocsV3Support(
String category,
String name,
Class<?> testClass,
Supplier<Map<List<DataType>, DataType>> signatures,
Supplier<Map<List<Param>, DataType>> signatures,
Callbacks callbacks
) {
this(category, name, null, testClass, signatures, callbacks);
Expand All @@ -391,7 +393,7 @@ private DocsV3Support(
String name,
FunctionDefinition definition,
Class<?> testClass,
Supplier<Map<List<DataType>, DataType>> signatures,
Supplier<Map<List<Param>, DataType>> signatures,
Callbacks callbacks
) {
this.category = category;
Expand Down Expand Up @@ -571,7 +573,7 @@ private FunctionDocsSupport(String name, Class<?> testClass, Callbacks callbacks
String name,
Class<?> testClass,
FunctionDefinition definition,
Supplier<Map<List<DataType>, DataType>> signatures,
Supplier<Map<List<Param>, DataType>> signatures,
Callbacks callbacks
) {
super("functions", name, definition, testClass, signatures, callbacks);
Expand Down Expand Up @@ -662,10 +664,27 @@ private void renderFunctionNamedParams(EsqlFunctionRegistry.MapArgSignature mapA
writeToTempSnippetsDir("functionNamedParams", rendered.toString());
}

private String makeAppliesToText(FunctionAppliesTo[] functionAppliesTos, boolean preview) {
/**
* Build the {@code {applies_to}} annotation for the docs to tell users which version of
* Elasticsearch first supported this function/operator/signature.
* @param functionAppliesTos The version information for stateful Elasticsearch
* @param preview Is this tech preview? Effectively just generates the
* {@code serverless: preview} annotation if true and nothing if false.
* @param oneLine Should we generate a single line variant of the {@code {applies_to}}
* annotation compatible with tables (true) or the more readable
* multi-line variant (false)?
* @return Text of the {@code {applies_to}} annotation
*/
private static String makeAppliesToText(List<FunctionAppliesTo> functionAppliesTos, boolean preview, boolean oneLine) {
StringBuilder appliesToText = new StringBuilder();
if (functionAppliesTos.length > 0) {
appliesToText.append("```{applies_to}\n");
if (false == functionAppliesTos.isEmpty()) {
if (oneLine) {
appliesToText.append(" {applies_to}");
appliesToText.append("`");
} else {
appliesToText.append("```");
appliesToText.append("{applies_to}\n");
}
StringBuilder stackEntries = new StringBuilder();

for (FunctionAppliesTo appliesTo : functionAppliesTos) {
Expand All @@ -680,15 +699,21 @@ private String makeAppliesToText(FunctionAppliesTo[] functionAppliesTos, boolean

// Add the stack entries
if (stackEntries.isEmpty() == false) {
appliesToText.append("stack: ").append(stackEntries).append("\n");
appliesToText.append("stack: ").append(stackEntries);
if (false == oneLine) {
appliesToText.append('\n');
}
}

// Only specify serverless if it's preview, using the preview boolean (GA is the default)
if (preview) {
appliesToText.append("serverless: preview\n");
appliesToText.append("serverless: preview");
if (false == oneLine) {
appliesToText.append('\n');
}
}

appliesToText.append("```\n");
appliesToText.append(oneLine ? "`" : "```\n");
}
return appliesToText.toString();
}
Expand All @@ -711,7 +736,7 @@ private void renderFullLayout(FunctionInfo info, boolean hasExamples, boolean ha
.replace("$NAME$", name)
.replace("$CATEGORY$", category)
.replace("$UPPER_NAME$", name.toUpperCase(Locale.ROOT))
.replace("$APPLIES_TO$", makeAppliesToText(info.appliesTo(), info.preview()))
.replace("$APPLIES_TO$", makeAppliesToText(Arrays.asList(info.appliesTo()), info.preview(), false))
);
for (String section : new String[] { "parameters", "description", "types" }) {
rendered.append(addInclude(section));
Expand Down Expand Up @@ -755,7 +780,7 @@ public OperatorsDocsSupport(
String name,
Class<?> testClass,
OperatorConfig op,
Supplier<Map<List<DataType>, DataType>> signatures,
Supplier<Map<List<Param>, DataType>> signatures,
Callbacks callbacks
) {
super("operators", name, testClass, signatures, callbacks);
Expand Down Expand Up @@ -888,7 +913,9 @@ void renderDocsForOperators(
if (mapParamInfo != null) {
args.add(mapParam(mapParamInfo));
} else {
Param paramInfo = params[i].getAnnotation(Param.class);
org.elasticsearch.xpack.esql.expression.function.Param paramInfo = params[i].getAnnotation(
org.elasticsearch.xpack.esql.expression.function.Param.class
);
args.add(paramInfo != null ? param(paramInfo, false) : paramWithoutAnnotation(params[i].getName()));
}
}
Expand Down Expand Up @@ -956,7 +983,7 @@ public CommandsDocsSupport(
Class<?> testClass,
LogicalPlan command,
List<EsqlFunctionRegistry.ArgSignature> args,
Supplier<Map<List<DataType>, DataType>> signatures,
Supplier<Map<List<Param>, DataType>> signatures,
Callbacks callbacks
) {
super("commands", name, testClass, signatures, callbacks);
Expand Down Expand Up @@ -1016,12 +1043,12 @@ void renderTypes(String name, List<EsqlFunctionRegistry.ArgSignature> args) thro
}

Map<String, List<String>> compactedTable = new TreeMap<>();
for (Map.Entry<List<DataType>, DataType> sig : this.signatures.get().entrySet()) {
for (Map.Entry<List<DocsV3Support.Param>, DataType> sig : this.signatures.get().entrySet()) {
if (shouldHideSignature(sig.getKey(), sig.getValue())) {
continue;
}
String mainType = sig.getKey().getFirst().esNameIfPossible();
String secondaryType = sig.getKey().get(1).esNameIfPossible();
String mainType = sig.getKey().getFirst().dataType().esNameIfPossible();
String secondaryType = sig.getKey().get(1).dataType().esNameIfPossible();
List<String> secondaryTypes = compactedTable.computeIfAbsent(mainType, (k) -> new ArrayList<>());
secondaryTypes.add(secondaryType);
}
Expand Down Expand Up @@ -1079,7 +1106,7 @@ void renderTypes(String name, List<EsqlFunctionRegistry.ArgSignature> args) thro
}

List<String> table = new ArrayList<>();
for (Map.Entry<List<DataType>, DataType> sig : this.signatures.get().entrySet()) { // TODO flip to using sortedSignatures
for (Map.Entry<List<DocsV3Support.Param>, DataType> sig : this.signatures.get().entrySet()) { // TODO flip to using sortedSignatures
if (shouldHideSignature(sig.getKey(), sig.getValue())) {
continue;
}
Expand All @@ -1104,18 +1131,21 @@ void renderTypes(String name, List<EsqlFunctionRegistry.ArgSignature> args) thro

private static String getTypeRow(
List<EsqlFunctionRegistry.ArgSignature> args,
Map.Entry<List<DataType>, DataType> sig,
Map.Entry<List<Param>, DataType> sig,
List<String> argNames,
boolean showResultColumn
) {
StringBuilder b = new StringBuilder("| ");
for (int i = 0; i < sig.getKey().size(); i++) {
DataType argType = sig.getKey().get(i);
Param param = sig.getKey().get(i);
EsqlFunctionRegistry.ArgSignature argSignature = args.get(i);
if (argSignature.mapArg()) {
b.append("named parameters");
} else {
b.append(argType.esNameIfPossible());
b.append(param.dataType().esNameIfPossible());
if (param.appliesTo() != null) {
b.append(FunctionDocsSupport.makeAppliesToText(param.appliesTo(), false, true));
}
}
b.append(" | ");
}
Expand Down Expand Up @@ -1274,7 +1304,7 @@ void renderKibanaFunctionDefinition(
builder.endObject();
} else {
int minArgCount = (int) args.stream().filter(a -> false == a.optional()).count();
for (Map.Entry<List<DataType>, DataType> sig : sortedSignatures()) {
for (Map.Entry<List<DocsV3Support.Param>, DataType> sig : sortedSignatures()) {
if (variadic && sig.getKey().size() > args.size()) {
// For variadic functions we test much longer signatures, let’s just stop at the last one
continue;
Expand Down Expand Up @@ -1302,15 +1332,15 @@ void renderKibanaFunctionDefinition(
.collect(Collectors.joining(", "))
);
} else {
builder.field("type", sig.getKey().get(i).esNameIfPossible());
builder.field("type", sig.getKey().get(i).dataType().esNameIfPossible());
}
builder.field("optional", arg.optional());
String cleanedParamDesc = removeAppliesToBlocks(arg.description());
builder.field("description", cleanedParamDesc);
builder.endObject();
}
builder.endArray();
license = licenseChecker.invoke(sig.getKey());
license = licenseChecker.invoke(sig.getKey().stream().map(Param::dataType).toList());
if (license != null && license != License.OperationMode.BASIC) {
builder.field("license", license.toString());
}
Expand Down Expand Up @@ -1358,8 +1388,8 @@ private static String removeAppliesToBlocks(String content) {
return content.replaceAll("\\s*\\{applies_to\\}`[^`]*`\\s*", "");
}

private List<Map.Entry<List<DataType>, DataType>> sortedSignatures() {
List<Map.Entry<List<DataType>, DataType>> sortedSignatures = new ArrayList<>(signatures.get().entrySet());
private List<Map.Entry<List<DocsV3Support.Param>, DataType>> sortedSignatures() {
List<Map.Entry<List<DocsV3Support.Param>, DataType>> sortedSignatures = new ArrayList<>(signatures.get().entrySet());
sortedSignatures.sort((lhs, rhs) -> {
int maxlen = Math.max(lhs.getKey().size(), rhs.getKey().size());
for (int i = 0; i < maxlen; i++) {
Expand All @@ -1369,7 +1399,7 @@ private List<Map.Entry<List<DataType>, DataType>> sortedSignatures() {
if (rhs.getKey().size() <= i) {
return 1;
}
int c = lhs.getKey().get(i).esNameIfPossible().compareTo(rhs.getKey().get(i).esNameIfPossible());
int c = lhs.getKey().get(i).dataType().esNameIfPossible().compareTo(rhs.getKey().get(i).dataType().esNameIfPossible());
if (c != 0) {
return c;
}
Expand Down
Loading
Loading