This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git

commit 2e786f6d1b442092a4f0ffe90571feae5174f926
Author: Robert Lazarski <[email protected]>
AuthorDate: Sun Apr 5 17:00:45 2026 -1000

    openapi: implement service/operation exclusion filtering
    
    Adds two new configuration properties to OpenApiConfiguration:
    
    ignoredServices (Set<String>)
      Exact service names to suppress from the generated spec. Checked in
      shouldIncludeService() before any inclusion rule, so a listed service
      is never emitted even when readAllResources=true.
      Properties key: openapi.ignoredServices (comma-separated)
    
    ignoredOperations (Set<String>)
      Per-operation exclusion with two match modes:
      - "ServiceName/operationName" — targeted: removes only that op on that 
service
      - "operationName" — global: removes that op name across every service
      Evaluated in the new shouldIncludeOperation() called from 
generateServicePaths()
      before the path is added to the Paths map.
      Properties key: openapi.ignoredOperations (comma-separated)
    
    applyPropertiesConfiguration() now also loads openapi.ignoredRoutes,
    openapi.ignoredServices, and openapi.ignoredOperations from any properties
    source (file, system properties, known scan locations).
    
    copy() propagates both new sets.
    
    Convenience API: addIgnoredService(String), addIgnoredOperation(String).
    
    Tests (5 new in OpenApiSpecGeneratorTest):
    - testIgnoredServiceIsExcluded
    - testIgnoredQualifiedOperationIsExcluded
    - testIgnoredBareOperationIsExcludedAcrossAllServices
    - testIgnoredServicesAndOperationsProgrammaticAPI (includes copy() check)
    - testQualifiedIgnoredOperationDoesNotAffectOtherServices
    
    Documentation: modules/openapi/README.md — full configuration reference
    covering all three exclusion mechanisms (ignoredServices, ignoredOperations,
    ignoredRoutes), precedence order, properties file format, and Java API 
examples.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 modules/openapi/README.md                          | 185 +++++++++++++++++++++
 .../apache/axis2/openapi/OpenApiConfiguration.java |  63 +++++++
 .../apache/axis2/openapi/OpenApiSpecGenerator.java |  35 ++++
 .../axis2/openapi/OpenApiSpecGeneratorTest.java    | 169 +++++++++++++++++++
 4 files changed, 452 insertions(+)

diff --git a/modules/openapi/README.md b/modules/openapi/README.md
new file mode 100644
index 0000000000..add52816ca
--- /dev/null
+++ b/modules/openapi/README.md
@@ -0,0 +1,185 @@
+# axis2-openapi — OpenAPI Integration Module
+
+Auto-generates OpenAPI 3.0.1 specifications from deployed Axis2 services and 
serves Swagger UI at `/swagger-ui`.
+
+## Endpoints
+
+| URL | Description |
+|-----|-------------|
+| `/openapi.json` | OpenAPI 3.0.1 spec (JSON) |
+| `/openapi.yaml` | OpenAPI 3.0.1 spec (YAML) |
+| `/swagger-ui` | Interactive Swagger UI |
+
+## Enabling the module
+
+Add to `WEB-INF/conf/axis2.xml`:
+```xml
+<module ref="openapi"/>
+```
+
+Copy `axis2-openapi-<version>.jar` to `WEB-INF/modules/openapi-<version>.mar`.
+
+---
+
+## Configuration
+
+Configuration is loaded in this order (later sources win):
+
+1. `module.xml` parameters
+2. `openapi.properties` on the classpath (or the path set by `propertiesFile` 
module param)
+3. Known locations: `META-INF/openapi.properties`, 
`WEB-INF/openapi.properties`, `openapi-config.properties`
+4. System properties (same key names)
+5. Programmatic `OpenApiConfiguration` API (highest precedence)
+
+### API information
+
+| Property key | Default | Description |
+|---|---|---|
+| `openapi.title` | `Apache Axis2 REST API` | Spec `info.title` |
+| `openapi.description` | (auto) | Spec `info.description` |
+| `openapi.version` | `1.0.0` | Spec `info.version` |
+| `openapi.contact.name` | `Apache Axis2` | Contact name |
+| `openapi.contact.url` | (Apache URL) | Contact URL |
+| `openapi.contact.email` | — | Contact e-mail |
+| `openapi.license.name` | `Apache License 2.0` | License name |
+| `openapi.license.url` | (Apache URL) | License URL |
+| `openapi.termsOfServiceUrl` | — | Terms of service URL |
+
+### Generation flags
+
+| Property key | Default | Description |
+|---|---|---|
+| `openapi.prettyPrint` | `true` | Indent JSON/YAML output |
+| `openapi.readAllResources` | `true` | Include all services unless filtered |
+| `openapi.swaggerUi.enabled` | `true` | Serve Swagger UI |
+| `openapi.swaggerUi.version` | `4.15.5` | CDN version of Swagger UI bundle |
+| `openapi.resourcePackages` | — | Comma-separated Java packages; only 
services whose `ServiceClass` is in these packages are included (requires 
`readAllResources=false`) |
+
+---
+
+## Filtering: excluding services and operations
+
+Three independent mechanisms control what appears in the generated spec. All 
are evaluated **before** inclusion rules — an excluded entity never appears 
even if it would otherwise match `readAllResources` or `resourcePackages`.
+
+### 1. `ignoredServices` — exclude entire services by name
+
+Matches against `AxisService.getName()` (exact, case-sensitive).
+
+**Properties file:**
+```properties
+# Comma-separated list of service names to exclude
+openapi.ignoredServices=InternalService, DebugService, AdminService
+```
+
+**Java API:**
+```java
+OpenApiConfiguration config = new OpenApiConfiguration();
+config.addIgnoredService("InternalService");
+config.addIgnoredService("DebugService");
+```
+
+### 2. `ignoredOperations` — exclude specific operations
+
+Each entry is one of:
+
+| Format | Effect |
+|---|---|
+| `ServiceName/operationName` | Excludes that operation on that service only |
+| `operationName` | Excludes that operation name on **every** service |
+
+**Properties file:**
+```properties
+# Targeted: remove one op from one service
+# Global: remove an op name from all services
+openapi.ignoredOperations=AdminService/nukeDatabase, internalStatus, debugPing
+```
+
+**Java API:**
+```java
+config.addIgnoredOperation("AdminService/nukeDatabase");  // targeted
+config.addIgnoredOperation("internalStatus");             // global (all 
services)
+```
+
+### 3. `ignoredRoutes` — exclude by generated path pattern
+
+Matches against the generated path string (e.g. `/services/MyService/myOp`).
+Each entry is tested as a Java regex (`String.matches()`) **or** a substring 
(`String.contains()`).
+
+```properties
+openapi.ignoredRoutes=/services/internal/.*, /services/legacy/.*
+```
+
+**Java API:**
+```java
+config.addIgnoredRoute("/services/internal/.*");
+```
+
+> **Prefer `ignoredServices` and `ignoredOperations`** over `ignoredRoutes` — 
they match on logical names rather than generated path strings and are 
unaffected by future path format changes.
+
+### Precedence within the generator
+
+```
+isSystemService()          (hardcoded: Version, AdminService, __)
+  → ignoredServices
+    → readAllResources / resourceClasses / resourcePackages
+      → shouldIncludeOperation() / ignoredOperations
+        → isIgnoredRoute() / ignoredRoutes
+          → (path added to spec)
+```
+
+### Complete `openapi.properties` example
+
+Place on the classpath as `openapi.properties` (or 
`META-INF/openapi.properties`):
+
+```properties
+# API identity
+openapi.title=My Financial API
+openapi.version=2.1.0
+openapi.description=Internal portfolio management services
+
+# Contact / license
+openapi.contact.name=Platform Team
[email protected]
+openapi.license.name=Proprietary
+
+# Service filtering
+openapi.ignoredServices=LegacySOAPService, InternalHealthCheck
+openapi.ignoredOperations=AdminService/resetDatabase, debugEcho
+
+# UI
+openapi.prettyPrint=true
+openapi.swaggerUi.enabled=true
+openapi.swaggerUi.version=4.15.5
+```
+
+---
+
+## Programmatic configuration (Java)
+
+```java
+OpenApiConfiguration config = new OpenApiConfiguration();
+
+// API info
+config.setTitle("My API");
+config.setVersion("2.0.0");
+
+// Exclude services
+config.addIgnoredService("InternalService");
+
+// Exclude operations
+config.addIgnoredOperation("AdminService/nukeDatabase"); // this service only
+config.addIgnoredOperation("debugPing");                 // all services
+
+// Pass to the generator
+OpenApiSpecGenerator generator = new OpenApiSpecGenerator(configContext, 
config);
+String json = generator.generateOpenApiJson(httpRequest);
+String yaml = generator.generateOpenApiYaml(httpRequest);
+```
+
+---
+
+## Known limitations
+
+- **Request body schema** — all operations are typed as `object` because Axis2 
JSON-RPC services use `JsonRpcMessageReceiver` and have no annotation-level 
parameter metadata. Schema details must be added via `OpenApiCustomizer` or by 
serving a static schema file.
+- **GET operations** — all operations are mapped to `POST`; override via 
`OpenApiCustomizer` if GET endpoints are needed.
+- **YAML format** — uses `jackson-dataformat-yaml` (transitive from 
`swagger-core`); no additional dependency required.
diff --git 
a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiConfiguration.java
 
b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiConfiguration.java
index 0bf1292163..be57f5661b 100644
--- 
a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiConfiguration.java
+++ 
b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiConfiguration.java
@@ -85,6 +85,26 @@ public class OpenApiConfiguration {
     /** Routes/paths to ignore during generation */
     private Collection<String> ignoredRoutes = new ArrayList<>();
 
+    /**
+     * Service names to exclude from the generated spec.
+     * Checked against {@link 
org.apache.axis2.description.AxisService#getName()}.
+     * Example: {@code ignoredServices = {"InternalService", "DebugService"}}
+     * Properties key: {@code openapi.ignoredServices} (comma-separated)
+     */
+    private Set<String> ignoredServices = new HashSet<>();
+
+    /**
+     * Operations to exclude from the generated spec.
+     * Each entry is either:
+     * <ul>
+     *   <li>{@code "ServiceName/operationName"} — excludes that operation on 
that service only</li>
+     *   <li>{@code "operationName"} — excludes that operation name across all 
services</li>
+     * </ul>
+     * Example: {@code ignoredOperations = {"AdminService/deleteAll", 
"internalStatus"}}
+     * Properties key: {@code openapi.ignoredOperations} (comma-separated)
+     */
+    private Set<String> ignoredOperations = new HashSet<>();
+
     /** Whether to scan known configuration locations */
     private boolean scanKnownConfigLocations = true;
 
@@ -243,6 +263,24 @@ public class OpenApiConfiguration {
         if (packages != null) {
             
resourcePackages.addAll(Arrays.asList(packages.split("\\s*,\\s*")));
         }
+
+        // Ignored routes (comma-separated path patterns)
+        String routes = getProperty(props, "openapi.ignoredRoutes", null);
+        if (routes != null) {
+            ignoredRoutes.addAll(Arrays.asList(routes.split("\\s*,\\s*")));
+        }
+
+        // Ignored service names (comma-separated exact names)
+        String services = getProperty(props, "openapi.ignoredServices", null);
+        if (services != null) {
+            ignoredServices.addAll(Arrays.asList(services.split("\\s*,\\s*")));
+        }
+
+        // Ignored operations (comma-separated, each "ServiceName/opName" or 
bare "opName")
+        String operations = getProperty(props, "openapi.ignoredOperations", 
null);
+        if (operations != null) {
+            
ignoredOperations.addAll(Arrays.asList(operations.split("\\s*,\\s*")));
+        }
     }
 
     /**
@@ -350,6 +388,12 @@ public class OpenApiConfiguration {
     public Collection<String> getIgnoredRoutes() { return ignoredRoutes; }
     public void setIgnoredRoutes(Collection<String> ignoredRoutes) { 
this.ignoredRoutes = ignoredRoutes; }
 
+    public Set<String> getIgnoredServices() { return ignoredServices; }
+    public void setIgnoredServices(Set<String> ignoredServices) { 
this.ignoredServices = ignoredServices; }
+
+    public Set<String> getIgnoredOperations() { return ignoredOperations; }
+    public void setIgnoredOperations(Set<String> ignoredOperations) { 
this.ignoredOperations = ignoredOperations; }
+
     public boolean isPrettyPrint() { return prettyPrint; }
     public void setPrettyPrint(boolean prettyPrint) { this.prettyPrint = 
prettyPrint; }
 
@@ -427,6 +471,23 @@ public class OpenApiConfiguration {
         ignoredRoutes.add(route);
     }
 
+    /**
+     * Exclude a service from the generated spec by its exact name.
+     * @param serviceName value of {@link 
org.apache.axis2.description.AxisService#getName()}
+     */
+    public void addIgnoredService(String serviceName) {
+        ignoredServices.add(serviceName);
+    }
+
+    /**
+     * Exclude an operation from the generated spec.
+     * @param entry either {@code "ServiceName/operationName"} (targeted) or
+     *              {@code "operationName"} (applies to every service)
+     */
+    public void addIgnoredOperation(String entry) {
+        ignoredOperations.add(entry);
+    }
+
     /**
      * Create a copy of this configuration.
      */
@@ -458,6 +519,8 @@ public class OpenApiConfiguration {
         copy.resourcePackages = new HashSet<>(this.resourcePackages);
         copy.resourceClasses = new HashSet<>(this.resourceClasses);
         copy.ignoredRoutes = new ArrayList<>(this.ignoredRoutes);
+        copy.ignoredServices = new HashSet<>(this.ignoredServices);
+        copy.ignoredOperations = new HashSet<>(this.ignoredOperations);
         copy.securityDefinitions = new HashMap<>(this.securityDefinitions);
         copy.swaggerUiMediaTypes = new HashMap<>(this.swaggerUiMediaTypes);
 
diff --git 
a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
 
b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
index 17ffd20368..a3f16faa7a 100644
--- 
a/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
+++ 
b/modules/openapi/src/main/java/org/apache/axis2/openapi/OpenApiSpecGenerator.java
@@ -337,6 +337,27 @@ public class OpenApiSpecGenerator {
         return paths;
     }
 
+    /**
+     * Check if an operation should be included based on {@code 
ignoredOperations}.
+     * An entry matches if it equals either:
+     * <ul>
+     *   <li>{@code "ServiceName/operationName"} — targeted exclusion for one 
service</li>
+     *   <li>{@code "operationName"} — excludes this operation name from every 
service</li>
+     * </ul>
+     */
+    private boolean shouldIncludeOperation(AxisService service, AxisOperation 
operation) {
+        String opName = operation.getName().getLocalPart();
+        String qualified = service.getName() + "/" + opName;
+
+        for (String entry : configuration.getIgnoredOperations()) {
+            if (entry.equals(qualified) || entry.equals(opName)) {
+                log.debug("Skipping operation excluded by ignoredOperations '" 
+ entry + "': " + qualified);
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Generate paths for a specific service.
      */
@@ -347,6 +368,11 @@ public class OpenApiSpecGenerator {
             while (operations.hasNext()) {
                 AxisOperation operation = operations.next();
 
+                // Check per-operation exclusion before generating the path
+                if (!shouldIncludeOperation(service, operation)) {
+                    continue;
+                }
+
                 // Generate path for REST operation
                 String path = generateOperationPath(service, operation);
                 if (path != null && !isIgnoredRoute(path)) {
@@ -439,9 +465,18 @@ public class OpenApiSpecGenerator {
 
     /**
      * Check if a service should be included based on configuration filters.
+     * Exclusion is evaluated before inclusion: a service listed in
+     * {@code ignoredServices} is always skipped regardless of other settings.
      */
     private boolean shouldIncludeService(AxisService service) {
         String serviceName = service.getName();
+
+        // Explicit exclusion by name takes priority over all other filters
+        if (configuration.getIgnoredServices().contains(serviceName)) {
+            log.debug("Skipping service explicitly excluded by 
ignoredServices: " + serviceName);
+            return false;
+        }
+
         String servicePackage = getServicePackage(service);
 
         // If readAllResources is false, check specific resource 
classes/packages
diff --git 
a/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
 
b/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
index 0ef5f01ae4..fe08d1c01b 100644
--- 
a/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
+++ 
b/modules/openapi/src/test/java/org/apache/axis2/openapi/OpenApiSpecGeneratorTest.java
@@ -235,6 +235,175 @@ public class OpenApiSpecGeneratorTest extends TestCase {
         // Note: Actual path structure depends on service configuration
     }
 
+    // ========== Service / Operation Exclusion Tests ==========
+
+    /**
+     * Test that a service listed in ignoredServices is omitted from the spec.
+     */
+    public void testIgnoredServiceIsExcluded() throws Exception {
+        // Arrange
+        AxisService visible = new AxisService("PublicService");
+        AxisOperation visibleOp = new 
org.apache.axis2.description.InOutAxisOperation();
+        visibleOp.setName(javax.xml.namespace.QName.valueOf("getData"));
+        visible.addOperation(visibleOp);
+
+        AxisService hidden = new AxisService("InternalService");
+        AxisOperation hiddenOp = new 
org.apache.axis2.description.InOutAxisOperation();
+        hiddenOp.setName(javax.xml.namespace.QName.valueOf("secretOp"));
+        hidden.addOperation(hiddenOp);
+
+        axisConfiguration.addService(visible);
+        axisConfiguration.addService(hidden);
+
+        OpenApiConfiguration config = new OpenApiConfiguration();
+        config.addIgnoredService("InternalService");
+        OpenApiSpecGenerator filtered = new 
OpenApiSpecGenerator(configurationContext, config);
+
+        // Act
+        OpenAPI openApi = filtered.generateOpenApiSpec(mockRequest);
+
+        // Assert
+        assertNotNull(openApi.getPaths());
+        assertNotNull("Public service path should be present",
+                openApi.getPaths().get("/services/PublicService/getData"));
+        assertNull("Hidden service path must be absent",
+                openApi.getPaths().get("/services/InternalService/secretOp"));
+    }
+
+    /**
+     * Test that a qualified "ServiceName/operationName" entry in 
ignoredOperations
+     * removes only that specific operation, leaving other operations on the 
same
+     * service in the spec.
+     */
+    public void testIgnoredQualifiedOperationIsExcluded() throws Exception {
+        // Arrange
+        AxisService svc = new AxisService("DataService");
+
+        AxisOperation keep = new 
org.apache.axis2.description.InOutAxisOperation();
+        keep.setName(javax.xml.namespace.QName.valueOf("publicQuery"));
+        svc.addOperation(keep);
+
+        AxisOperation drop = new 
org.apache.axis2.description.InOutAxisOperation();
+        drop.setName(javax.xml.namespace.QName.valueOf("adminDump"));
+        svc.addOperation(drop);
+
+        axisConfiguration.addService(svc);
+
+        OpenApiConfiguration config = new OpenApiConfiguration();
+        config.addIgnoredOperation("DataService/adminDump");
+        OpenApiSpecGenerator filtered = new 
OpenApiSpecGenerator(configurationContext, config);
+
+        // Act
+        OpenAPI openApi = filtered.generateOpenApiSpec(mockRequest);
+
+        // Assert
+        assertNotNull("publicQuery must remain", 
openApi.getPaths().get("/services/DataService/publicQuery"));
+        assertNull("adminDump must be excluded", 
openApi.getPaths().get("/services/DataService/adminDump"));
+    }
+
+    /**
+     * Test that a bare operation name (no service prefix) in ignoredOperations
+     * suppresses that operation across every service.
+     */
+    public void testIgnoredBareOperationIsExcludedAcrossAllServices() throws 
Exception {
+        // Arrange — two services each with the banned operation name
+        AxisService svc1 = new AxisService("ServiceAlpha");
+        AxisOperation alpha_good = new 
org.apache.axis2.description.InOutAxisOperation();
+        alpha_good.setName(javax.xml.namespace.QName.valueOf("doWork"));
+        svc1.addOperation(alpha_good);
+        AxisOperation alpha_bad = new 
org.apache.axis2.description.InOutAxisOperation();
+        alpha_bad.setName(javax.xml.namespace.QName.valueOf("internalStatus"));
+        svc1.addOperation(alpha_bad);
+
+        AxisService svc2 = new AxisService("ServiceBeta");
+        AxisOperation beta_good = new 
org.apache.axis2.description.InOutAxisOperation();
+        beta_good.setName(javax.xml.namespace.QName.valueOf("doWork"));
+        svc2.addOperation(beta_good);
+        AxisOperation beta_bad = new 
org.apache.axis2.description.InOutAxisOperation();
+        beta_bad.setName(javax.xml.namespace.QName.valueOf("internalStatus"));
+        svc2.addOperation(beta_bad);
+
+        axisConfiguration.addService(svc1);
+        axisConfiguration.addService(svc2);
+
+        OpenApiConfiguration config = new OpenApiConfiguration();
+        config.addIgnoredOperation("internalStatus"); // bare name — applies 
to both services
+        OpenApiSpecGenerator filtered = new 
OpenApiSpecGenerator(configurationContext, config);
+
+        // Act
+        OpenAPI openApi = filtered.generateOpenApiSpec(mockRequest);
+
+        // Assert — doWork present on both, internalStatus absent on both
+        assertNotNull(openApi.getPaths().get("/services/ServiceAlpha/doWork"));
+        assertNotNull(openApi.getPaths().get("/services/ServiceBeta/doWork"));
+        assertNull("internalStatus must be absent from ServiceAlpha",
+                
openApi.getPaths().get("/services/ServiceAlpha/internalStatus"));
+        assertNull("internalStatus must be absent from ServiceBeta",
+                
openApi.getPaths().get("/services/ServiceBeta/internalStatus"));
+    }
+
+    /**
+     * Test that ignoredServices and ignoredOperations are populated correctly
+     * via the programmatic API (the same code path exercised by properties 
loading
+     * once applyPropertiesConfiguration parses the values).
+     */
+    public void testIgnoredServicesAndOperationsProgrammaticAPI() throws 
Exception {
+        OpenApiConfiguration config = new OpenApiConfiguration();
+        config.addIgnoredService("SecretService");
+        config.addIgnoredService("HiddenService");
+        config.addIgnoredOperation("AdminService/nukeDatabase");
+        config.addIgnoredOperation("debugPing");
+
+        assertTrue("SecretService should be in ignoredServices",
+                config.getIgnoredServices().contains("SecretService"));
+        assertTrue("HiddenService should be in ignoredServices",
+                config.getIgnoredServices().contains("HiddenService"));
+        assertTrue("AdminService/nukeDatabase should be in ignoredOperations",
+                
config.getIgnoredOperations().contains("AdminService/nukeDatabase"));
+        assertTrue("debugPing should be in ignoredOperations",
+                config.getIgnoredOperations().contains("debugPing"));
+
+        // Verify copy() preserves exclusion sets
+        OpenApiConfiguration copy = config.copy();
+        assertTrue("copy must preserve ignoredServices",
+                copy.getIgnoredServices().contains("SecretService"));
+        assertTrue("copy must preserve ignoredOperations",
+                copy.getIgnoredOperations().contains("debugPing"));
+    }
+
+    /**
+     * Test that a qualified operation entry does NOT suppress the same 
operation
+     * name on a different service (targeted exclusion, not global).
+     */
+    public void testQualifiedIgnoredOperationDoesNotAffectOtherServices() 
throws Exception {
+        // Arrange
+        AxisService svcA = new AxisService("ServiceA");
+        AxisOperation opA = new 
org.apache.axis2.description.InOutAxisOperation();
+        opA.setName(javax.xml.namespace.QName.valueOf("sensitiveOp"));
+        svcA.addOperation(opA);
+
+        AxisService svcB = new AxisService("ServiceB");
+        AxisOperation opB = new 
org.apache.axis2.description.InOutAxisOperation();
+        opB.setName(javax.xml.namespace.QName.valueOf("sensitiveOp")); // same 
name, different service
+        svcB.addOperation(opB);
+
+        axisConfiguration.addService(svcA);
+        axisConfiguration.addService(svcB);
+
+        OpenApiConfiguration config = new OpenApiConfiguration();
+        config.addIgnoredOperation("ServiceA/sensitiveOp"); // target ServiceA 
only
+        OpenApiSpecGenerator filtered = new 
OpenApiSpecGenerator(configurationContext, config);
+
+        // Act
+        OpenAPI openApi = filtered.generateOpenApiSpec(mockRequest);
+
+        // Assert
+        assertNull("ServiceA/sensitiveOp must be excluded",
+                openApi.getPaths().get("/services/ServiceA/sensitiveOp"));
+        assertNotNull("ServiceB/sensitiveOp must remain (different service)",
+                openApi.getPaths().get("/services/ServiceB/sensitiveOp"));
+    }
+
     /**
      * Test that generated JSON contains no null fields.
      * Jackson must be configured with Include.NON_NULL so null-valued model

Reply via email to