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,46 @@ 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 -> License .OperationMode .BASIC ;
298+ }
299+ }
300+
301+ public License .OperationMode invoke (List <DataType > fieldTypes ) throws Exception {
302+ if (staticMethod != null ) {
303+ return (License .OperationMode ) staticMethod .invoke (null , fieldTypes );
304+ } else {
305+ return fallbackLambda .apply (fieldTypes );
306+ }
307+ }
308+ }
309+
275310 protected final String category ;
276311 protected final String name ;
277312 protected final FunctionDefinition definition ;
278313 protected final Logger logger ;
279314 private final Supplier <Map <List <DataType >, DataType >> signatures ;
280315 private TempFileWriter tempFileWriter ;
316+ private final LicenseRequirementChecker licenseChecker ;
281317
282318 protected DocsV3Support (String category , String name , Class <?> testClass , Supplier <Map <List <DataType >, DataType >> signatures ) {
283319 this (category , name , null , testClass , signatures );
@@ -296,6 +332,7 @@ private DocsV3Support(
296332 this .logger = LogManager .getLogger (testClass );
297333 this .signatures = signatures ;
298334 this .tempFileWriter = new DocsFileWriter ();
335+ this .licenseChecker = new LicenseRequirementChecker (testClass );
299336 }
300337
301338 /** Used in tests to capture output for asserting on the content */
@@ -460,7 +497,7 @@ void writeToTempKibanaDir(String subdir, String extension, String str) throws IO
460497
461498 protected abstract void renderSignature () throws IOException ;
462499
463- protected abstract void renderDocs () throws IOException ;
500+ protected abstract void renderDocs () throws Exception ;
464501
465502 static class FunctionDocsSupport extends DocsV3Support {
466503 private FunctionDocsSupport (String name , Class <?> testClass ) {
@@ -488,7 +525,7 @@ protected void renderSignature() throws IOException {
488525 }
489526
490527 @ Override
491- protected void renderDocs () throws IOException {
528+ protected void renderDocs () throws Exception {
492529 if (definition == null ) {
493530 logger .info ("Skipping rendering docs because the function '{}' isn't registered" , name );
494531 } else {
@@ -497,7 +534,7 @@ protected void renderDocs() throws IOException {
497534 }
498535 }
499536
500- private void renderDocs (FunctionDefinition definition ) throws IOException {
537+ private void renderDocs (FunctionDefinition definition ) throws Exception {
501538 EsqlFunctionRegistry .FunctionDescription description = EsqlFunctionRegistry .description (definition );
502539 if (name .equals ("case" )) {
503540 /*
@@ -711,7 +748,7 @@ public void renderSignature() throws IOException {
711748 }
712749
713750 @ Override
714- public void renderDocs () throws IOException {
751+ public void renderDocs () throws Exception {
715752 Constructor <?> ctor = constructorWithFunctionInfo (op .clazz ());
716753 if (ctor != null ) {
717754 FunctionInfo functionInfo = ctor .getAnnotation (FunctionInfo .class );
@@ -722,7 +759,7 @@ public void renderDocs() throws IOException {
722759 }
723760 }
724761
725- void renderDocsForNegatedOperators (Constructor <?> ctor , Function <String , String > description ) throws IOException {
762+ void renderDocsForNegatedOperators (Constructor <?> ctor , Function <String , String > description ) throws Exception {
726763 String baseName = name .toLowerCase (Locale .ROOT ).replace ("not " , "" );
727764 OperatorConfig op = OPERATORS .get (baseName );
728765 assert op != null ;
@@ -795,7 +832,7 @@ public Example[] examples() {
795832 }
796833
797834 void renderDocsForOperators (String name , String titleName , Constructor <?> ctor , FunctionInfo info , boolean variadic )
798- throws IOException {
835+ throws Exception {
799836 renderKibanaInlineDocs (name , titleName , info );
800837
801838 var params = ctor .getParameters ();
@@ -999,7 +1036,7 @@ void renderKibanaFunctionDefinition(
9991036 FunctionInfo info ,
10001037 List <EsqlFunctionRegistry .ArgSignature > args ,
10011038 boolean variadic
1002- ) throws IOException {
1039+ ) throws Exception {
10031040
10041041 try (XContentBuilder builder = JsonXContent .contentBuilder ().prettyPrint ().lfAtEnd ().startObject ()) {
10051042 builder .field (
@@ -1019,6 +1056,10 @@ void renderKibanaFunctionDefinition(
10191056 });
10201057 }
10211058 builder .field ("name" , name );
1059+ License .OperationMode license = licenseChecker .invoke (null );
1060+ if (license != null && license != License .OperationMode .BASIC ) {
1061+ builder .field ("license" , license .toString ());
1062+ }
10221063 if (titleName != null && titleName .equals (name ) == false ) {
10231064 builder .field ("titleName" , titleName );
10241065 }
@@ -1073,6 +1114,10 @@ void renderKibanaFunctionDefinition(
10731114 builder .endObject ();
10741115 }
10751116 builder .endArray ();
1117+ license = licenseChecker .invoke (sig .getKey ());
1118+ if (license != null && license != License .OperationMode .BASIC ) {
1119+ builder .field ("license" , license .toString ());
1120+ }
10761121 builder .field ("variadic" , variadic );
10771122 builder .field ("returnType" , sig .getValue ().esNameIfPossible ());
10781123 builder .endObject ();
0 commit comments