1111
1212import org .elasticsearch .common .Strings ;
1313import org .elasticsearch .core .PathUtils ;
14+ import org .elasticsearch .license .License ;
1415import org .elasticsearch .logging .LogManager ;
1516import org .elasticsearch .logging .Logger ;
1617import org .elasticsearch .xcontent .XContentBuilder ;
4647import java .io .InputStreamReader ;
4748import java .lang .annotation .Annotation ;
4849import java .lang .reflect .Constructor ;
50+ import java .lang .reflect .Method ;
4951import java .nio .charset .StandardCharsets ;
5052import java .nio .file .Files ;
5153import java .nio .file .Path ;
@@ -107,7 +109,7 @@ static OperatorsDocsSupport forOperators(String name, Class<?> testClass) {
107109 return new OperatorsDocsSupport (name , testClass );
108110 }
109111
110- static void renderDocs (String name , Class <?> testClass ) throws IOException {
112+ static void renderDocs (String name , Class <?> testClass ) throws Exception {
111113 if (OPERATORS .containsKey (name )) {
112114 var docs = DocsV3Support .forOperators (name , testClass );
113115 docs .renderSignature ();
@@ -126,7 +128,7 @@ public static void renderNegatedOperator(
126128 String name ,
127129 Function <String , String > description ,
128130 Class <?> testClass
129- ) throws IOException {
131+ ) throws Exception {
130132 var docs = forOperators ("not " + name .toLowerCase (Locale .ROOT ), testClass );
131133 docs .renderDocsForNegatedOperators (ctor , description );
132134 }
@@ -272,12 +274,49 @@ public void writeToTempDir(Path dir, String extension, String str) throws IOExce
272274 }
273275 }
274276
277+ /**
278+ * This class is used to check if a license requirement method exists in the test class.
279+ * This is used to add license requirement information to the generated documentation.
280+ */
281+ public static class LicenseRequirementChecker {
282+ private Method staticMethod ;
283+ private Function <List <DataType >, License .OperationMode > fallbackLambda ;
284+
285+ public LicenseRequirementChecker (Class <?> testClass ) {
286+ try {
287+ staticMethod = testClass .getMethod ("licenseRequirement" , List .class );
288+ if (License .OperationMode .class .equals (staticMethod .getReturnType ()) == false
289+ || java .lang .reflect .Modifier .isStatic (staticMethod .getModifiers ()) == false ) {
290+ staticMethod = null ; // Reset if the method doesn't match the signature
291+ }
292+ } catch (NoSuchMethodException e ) {
293+ staticMethod = null ;
294+ }
295+
296+ if (staticMethod == null ) {
297+ fallbackLambda = fieldTypes -> {
298+ // Provide your default implementation here
299+ return License .OperationMode .BASIC ;
300+ };
301+ }
302+ }
303+
304+ public License .OperationMode invoke (List <DataType > fieldTypes ) throws Exception {
305+ if (staticMethod != null ) {
306+ return (License .OperationMode ) staticMethod .invoke (null , fieldTypes );
307+ } else {
308+ return fallbackLambda .apply (fieldTypes );
309+ }
310+ }
311+ }
312+
275313 protected final String category ;
276314 protected final String name ;
277315 protected final FunctionDefinition definition ;
278316 protected final Logger logger ;
279317 private final Supplier <Map <List <DataType >, DataType >> signatures ;
280318 private TempFileWriter tempFileWriter ;
319+ private final LicenseRequirementChecker licenseChecker ;
281320
282321 protected DocsV3Support (String category , String name , Class <?> testClass , Supplier <Map <List <DataType >, DataType >> signatures ) {
283322 this (category , name , null , testClass , signatures );
@@ -296,6 +335,7 @@ private DocsV3Support(
296335 this .logger = LogManager .getLogger (testClass );
297336 this .signatures = signatures ;
298337 this .tempFileWriter = new DocsFileWriter ();
338+ this .licenseChecker = new LicenseRequirementChecker (testClass );
299339 }
300340
301341 /** Used in tests to capture output for asserting on the content */
@@ -460,7 +500,7 @@ void writeToTempKibanaDir(String subdir, String extension, String str) throws IO
460500
461501 protected abstract void renderSignature () throws IOException ;
462502
463- protected abstract void renderDocs () throws IOException ;
503+ protected abstract void renderDocs () throws Exception ;
464504
465505 static class FunctionDocsSupport extends DocsV3Support {
466506 private FunctionDocsSupport (String name , Class <?> testClass ) {
@@ -488,7 +528,7 @@ protected void renderSignature() throws IOException {
488528 }
489529
490530 @ Override
491- protected void renderDocs () throws IOException {
531+ protected void renderDocs () throws Exception {
492532 if (definition == null ) {
493533 logger .info ("Skipping rendering docs because the function '{}' isn't registered" , name );
494534 } else {
@@ -497,7 +537,7 @@ protected void renderDocs() throws IOException {
497537 }
498538 }
499539
500- private void renderDocs (FunctionDefinition definition ) throws IOException {
540+ private void renderDocs (FunctionDefinition definition ) throws Exception {
501541 EsqlFunctionRegistry .FunctionDescription description = EsqlFunctionRegistry .description (definition );
502542 if (name .equals ("case" )) {
503543 /*
@@ -711,7 +751,7 @@ public void renderSignature() throws IOException {
711751 }
712752
713753 @ Override
714- public void renderDocs () throws IOException {
754+ public void renderDocs () throws Exception {
715755 Constructor <?> ctor = constructorWithFunctionInfo (op .clazz ());
716756 if (ctor != null ) {
717757 FunctionInfo functionInfo = ctor .getAnnotation (FunctionInfo .class );
@@ -722,7 +762,7 @@ public void renderDocs() throws IOException {
722762 }
723763 }
724764
725- void renderDocsForNegatedOperators (Constructor <?> ctor , Function <String , String > description ) throws IOException {
765+ void renderDocsForNegatedOperators (Constructor <?> ctor , Function <String , String > description ) throws Exception {
726766 String baseName = name .toLowerCase (Locale .ROOT ).replace ("not " , "" );
727767 OperatorConfig op = OPERATORS .get (baseName );
728768 assert op != null ;
@@ -795,7 +835,7 @@ public Example[] examples() {
795835 }
796836
797837 void renderDocsForOperators (String name , String titleName , Constructor <?> ctor , FunctionInfo info , boolean variadic )
798- throws IOException {
838+ throws Exception {
799839 renderKibanaInlineDocs (name , titleName , info );
800840
801841 var params = ctor .getParameters ();
@@ -999,7 +1039,7 @@ void renderKibanaFunctionDefinition(
9991039 FunctionInfo info ,
10001040 List <EsqlFunctionRegistry .ArgSignature > args ,
10011041 boolean variadic
1002- ) throws IOException {
1042+ ) throws Exception {
10031043
10041044 try (XContentBuilder builder = JsonXContent .contentBuilder ().prettyPrint ().lfAtEnd ().startObject ()) {
10051045 builder .field (
@@ -1019,6 +1059,10 @@ void renderKibanaFunctionDefinition(
10191059 });
10201060 }
10211061 builder .field ("name" , name );
1062+ License .OperationMode license = licenseChecker .invoke (null );
1063+ if (license != null && license != License .OperationMode .BASIC ) {
1064+ builder .field ("license" , license .toString ());
1065+ }
10221066 if (titleName != null && titleName .equals (name ) == false ) {
10231067 builder .field ("titleName" , titleName );
10241068 }
@@ -1073,6 +1117,10 @@ void renderKibanaFunctionDefinition(
10731117 builder .endObject ();
10741118 }
10751119 builder .endArray ();
1120+ license = licenseChecker .invoke (sig .getKey ());
1121+ if (license != null && license != License .OperationMode .BASIC ) {
1122+ builder .field ("license" , license .toString ());
1123+ }
10761124 builder .field ("variadic" , variadic );
10771125 builder .field ("returnType" , sig .getValue ().esNameIfPossible ());
10781126 builder .endObject ();
0 commit comments