|
25 | 25 | import com.getaxonflow.sdk.types.policies.PolicyTypes.*; |
26 | 26 | import com.getaxonflow.sdk.masfeat.MASFEATTypes.*; |
27 | 27 | import com.getaxonflow.sdk.types.webhook.WebhookTypes.*; |
| 28 | +import com.getaxonflow.sdk.simulation.*; |
28 | 29 | import com.getaxonflow.sdk.util.*; |
29 | 30 | import com.fasterxml.jackson.core.JsonProcessingException; |
30 | 31 | import com.fasterxml.jackson.core.type.TypeReference; |
@@ -821,6 +822,193 @@ public CompletableFuture<CircuitBreakerConfigUpdateResponse> updateCircuitBreake |
821 | 822 | return CompletableFuture.supplyAsync(() -> updateCircuitBreakerConfig(config), asyncExecutor); |
822 | 823 | } |
823 | 824 |
|
| 825 | + // ======================================================================== |
| 826 | + // Policy Simulation |
| 827 | + // ======================================================================== |
| 828 | + |
| 829 | + /** |
| 830 | + * Simulates policy evaluation against a query without actually enforcing policies. |
| 831 | + * |
| 832 | + * <p>This is a dry-run mode that shows which policies would match and what actions |
| 833 | + * would be taken, without blocking the request. |
| 834 | + * |
| 835 | + * <p>Example usage: |
| 836 | + * <pre>{@code |
| 837 | + * SimulatePoliciesResponse result = axonflow.simulatePolicies( |
| 838 | + * SimulatePoliciesRequest.builder() |
| 839 | + * .query("Transfer $50,000 to external account") |
| 840 | + * .requestType("execute") |
| 841 | + * .build()); |
| 842 | + * System.out.println("Allowed: " + result.isAllowed()); |
| 843 | + * System.out.println("Applied policies: " + result.getAppliedPolicies()); |
| 844 | + * System.out.println("Risk score: " + result.getRiskScore()); |
| 845 | + * }</pre> |
| 846 | + * |
| 847 | + * <p><b>Evaluation+ Feature:</b> Requires AxonFlow Evaluation tier or higher. |
| 848 | + * |
| 849 | + * @param request the simulation request |
| 850 | + * @return the simulation result |
| 851 | + * @throws NullPointerException if request is null |
| 852 | + * @throws AxonFlowException if the request fails |
| 853 | + */ |
| 854 | + public SimulatePoliciesResponse simulatePolicies(SimulatePoliciesRequest request) { |
| 855 | + Objects.requireNonNull(request, "request cannot be null"); |
| 856 | + |
| 857 | + return retryExecutor.execute(() -> { |
| 858 | + Request httpRequest = buildOrchestratorRequest("POST", "/api/v1/policies/simulate", request); |
| 859 | + try (Response response = httpClient.newCall(httpRequest).execute()) { |
| 860 | + JsonNode node = parseResponseNode(response); |
| 861 | + if (node.has("data") && node.get("data").isObject()) { |
| 862 | + return objectMapper.treeToValue(node.get("data"), SimulatePoliciesResponse.class); |
| 863 | + } |
| 864 | + return objectMapper.treeToValue(node, SimulatePoliciesResponse.class); |
| 865 | + } |
| 866 | + }, "simulatePolicies"); |
| 867 | + } |
| 868 | + |
| 869 | + /** |
| 870 | + * Asynchronously simulates policy evaluation against a query. |
| 871 | + * |
| 872 | + * @param request the simulation request |
| 873 | + * @return a future containing the simulation result |
| 874 | + */ |
| 875 | + public CompletableFuture<SimulatePoliciesResponse> simulatePoliciesAsync(SimulatePoliciesRequest request) { |
| 876 | + return CompletableFuture.supplyAsync(() -> simulatePolicies(request), asyncExecutor); |
| 877 | + } |
| 878 | + |
| 879 | + /** |
| 880 | + * Generates a policy impact report by testing a set of inputs against a specific policy. |
| 881 | + * |
| 882 | + * <p>This helps you understand how a policy would affect real traffic before deploying it. |
| 883 | + * |
| 884 | + * <p>Example usage: |
| 885 | + * <pre>{@code |
| 886 | + * ImpactReportResponse report = axonflow.getPolicyImpactReport( |
| 887 | + * ImpactReportRequest.builder() |
| 888 | + * .policyId("policy_block_pii") |
| 889 | + * .inputs(List.of( |
| 890 | + * ImpactReportInput.builder().query("My SSN is 123-45-6789").build(), |
| 891 | + * ImpactReportInput.builder().query("What is the weather?").build())) |
| 892 | + * .build()); |
| 893 | + * System.out.println("Match rate: " + report.getMatchRate()); |
| 894 | + * System.out.println("Block rate: " + report.getBlockRate()); |
| 895 | + * }</pre> |
| 896 | + * |
| 897 | + * <p><b>Evaluation+ Feature:</b> Requires AxonFlow Evaluation tier or higher. |
| 898 | + * |
| 899 | + * @param request the impact report request |
| 900 | + * @return the impact report |
| 901 | + * @throws NullPointerException if request is null |
| 902 | + * @throws AxonFlowException if the request fails |
| 903 | + */ |
| 904 | + public ImpactReportResponse getPolicyImpactReport(ImpactReportRequest request) { |
| 905 | + Objects.requireNonNull(request, "request cannot be null"); |
| 906 | + |
| 907 | + return retryExecutor.execute(() -> { |
| 908 | + Request httpRequest = buildOrchestratorRequest("POST", "/api/v1/policies/impact-report", request); |
| 909 | + try (Response response = httpClient.newCall(httpRequest).execute()) { |
| 910 | + JsonNode node = parseResponseNode(response); |
| 911 | + if (node.has("data") && node.get("data").isObject()) { |
| 912 | + return objectMapper.treeToValue(node.get("data"), ImpactReportResponse.class); |
| 913 | + } |
| 914 | + return objectMapper.treeToValue(node, ImpactReportResponse.class); |
| 915 | + } |
| 916 | + }, "getPolicyImpactReport"); |
| 917 | + } |
| 918 | + |
| 919 | + /** |
| 920 | + * Asynchronously generates a policy impact report. |
| 921 | + * |
| 922 | + * @param request the impact report request |
| 923 | + * @return a future containing the impact report |
| 924 | + */ |
| 925 | + public CompletableFuture<ImpactReportResponse> getPolicyImpactReportAsync(ImpactReportRequest request) { |
| 926 | + return CompletableFuture.supplyAsync(() -> getPolicyImpactReport(request), asyncExecutor); |
| 927 | + } |
| 928 | + |
| 929 | + /** |
| 930 | + * Scans all active policies for conflicts. |
| 931 | + * |
| 932 | + * <p>Example usage: |
| 933 | + * <pre>{@code |
| 934 | + * PolicyConflictResponse conflicts = axonflow.detectPolicyConflicts(); |
| 935 | + * System.out.println("Conflicts found: " + conflicts.getConflictCount()); |
| 936 | + * for (PolicyConflict conflict : conflicts.getConflicts()) { |
| 937 | + * System.out.println(conflict.getConflictType() + ": " + conflict.getDescription()); |
| 938 | + * } |
| 939 | + * }</pre> |
| 940 | + * |
| 941 | + * <p><b>Evaluation+ Feature:</b> Requires AxonFlow Evaluation tier or higher. |
| 942 | + * |
| 943 | + * @return the conflict detection result |
| 944 | + * @throws AxonFlowException if the request fails |
| 945 | + */ |
| 946 | + public PolicyConflictResponse detectPolicyConflicts() { |
| 947 | + return detectPolicyConflicts(null); |
| 948 | + } |
| 949 | + |
| 950 | + /** |
| 951 | + * Detects conflicts between a specific policy and other active policies, |
| 952 | + * or scans all policies if policyId is null. |
| 953 | + * |
| 954 | + * <p>Example usage: |
| 955 | + * <pre>{@code |
| 956 | + * PolicyConflictResponse conflicts = axonflow.detectPolicyConflicts("policy_block_pii"); |
| 957 | + * System.out.println("Conflicts found: " + conflicts.getConflictCount()); |
| 958 | + * for (PolicyConflict conflict : conflicts.getConflicts()) { |
| 959 | + * System.out.println(conflict.getConflictType() + ": " + conflict.getDescription()); |
| 960 | + * } |
| 961 | + * }</pre> |
| 962 | + * |
| 963 | + * <p><b>Evaluation+ Feature:</b> Requires AxonFlow Evaluation tier or higher. |
| 964 | + * |
| 965 | + * @param policyId the policy ID to check for conflicts, or null to scan all policies |
| 966 | + * @return the conflict detection result |
| 967 | + * @throws IllegalArgumentException if policyId is non-null and empty |
| 968 | + * @throws AxonFlowException if the request fails |
| 969 | + */ |
| 970 | + public PolicyConflictResponse detectPolicyConflicts(String policyId) { |
| 971 | + if (policyId != null && policyId.isEmpty()) { |
| 972 | + throw new IllegalArgumentException("policyId cannot be empty"); |
| 973 | + } |
| 974 | + |
| 975 | + return retryExecutor.execute(() -> { |
| 976 | + Object body; |
| 977 | + if (policyId != null) { |
| 978 | + body = java.util.Map.of("policy_id", policyId); |
| 979 | + } else { |
| 980 | + body = java.util.Map.of(); |
| 981 | + } |
| 982 | + Request httpRequest = buildOrchestratorRequest("POST", "/api/v1/policies/conflicts", body); |
| 983 | + try (Response response = httpClient.newCall(httpRequest).execute()) { |
| 984 | + JsonNode node = parseResponseNode(response); |
| 985 | + if (node.has("data") && node.get("data").isObject()) { |
| 986 | + return objectMapper.treeToValue(node.get("data"), PolicyConflictResponse.class); |
| 987 | + } |
| 988 | + return objectMapper.treeToValue(node, PolicyConflictResponse.class); |
| 989 | + } |
| 990 | + }, "detectPolicyConflicts"); |
| 991 | + } |
| 992 | + |
| 993 | + /** |
| 994 | + * Asynchronously scans all active policies for conflicts. |
| 995 | + * |
| 996 | + * @return a future containing the conflict detection result |
| 997 | + */ |
| 998 | + public CompletableFuture<PolicyConflictResponse> detectPolicyConflictsAsync() { |
| 999 | + return CompletableFuture.supplyAsync(() -> detectPolicyConflicts(), asyncExecutor); |
| 1000 | + } |
| 1001 | + |
| 1002 | + /** |
| 1003 | + * Asynchronously detects conflicts between a specific policy and other active policies. |
| 1004 | + * |
| 1005 | + * @param policyId the policy ID to check for conflicts, or null to scan all policies |
| 1006 | + * @return a future containing the conflict detection result |
| 1007 | + */ |
| 1008 | + public CompletableFuture<PolicyConflictResponse> detectPolicyConflictsAsync(String policyId) { |
| 1009 | + return CompletableFuture.supplyAsync(() -> detectPolicyConflicts(policyId), asyncExecutor); |
| 1010 | + } |
| 1011 | + |
824 | 1012 | // ======================================================================== |
825 | 1013 | // Proxy Mode - Query Execution |
826 | 1014 | // ======================================================================== |
|
0 commit comments