Skip to content

Commit 299fc8b

Browse files
author
github-actions
committed
chore: add query audit logs support
1 parent afe5e56 commit 299fc8b

File tree

11 files changed

+764
-2
lines changed

11 files changed

+764
-2
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package io.getstream.client;
2+
3+
import io.getstream.core.Stream;
4+
import io.getstream.core.http.Token;
5+
import io.getstream.core.exceptions.StreamException;
6+
import io.getstream.core.models.AuditLog;
7+
import io.getstream.core.options.RequestOption;
8+
import io.getstream.core.options.CustomQueryParameter;
9+
import io.getstream.core.utils.Auth.TokenAction;
10+
import java8.util.concurrent.CompletableFuture;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
15+
import static io.getstream.core.utils.Auth.buildAuditLogsToken;
16+
17+
/**
18+
* Client for querying Stream audit logs.
19+
* Audit logs record changes to various entities within your Stream app.
20+
*/
21+
public final class AuditLogsClient {
22+
private final String secret;
23+
private final Stream stream;
24+
25+
public AuditLogsClient(String secret, Stream stream) {
26+
this.secret = secret;
27+
this.stream = stream;
28+
}
29+
30+
/**
31+
* Query audit logs with the specified filters and default pagination.
32+
*
33+
* @param filters Filters to apply to the query (either entityType+entityID OR userID is required)
34+
* @return CompletableFuture with the query response
35+
* @throws StreamException if the filters are invalid or if there's an API error
36+
*/
37+
public CompletableFuture<QueryAuditLogsResponse> queryAuditLogs(QueryAuditLogsFilters filters) throws StreamException {
38+
return queryAuditLogs(filters, new QueryAuditLogsPager());
39+
}
40+
41+
/**
42+
* Query audit logs with the specified filters and pagination.
43+
*
44+
* @param filters Filters to apply to the query (either entityType+entityID OR userID is required)
45+
* @param pager Pagination settings for the query
46+
* @return CompletableFuture with the query response
47+
* @throws StreamException if the filters are invalid or if there's an API error
48+
*/
49+
public CompletableFuture<QueryAuditLogsResponse> queryAuditLogs(QueryAuditLogsFilters filters, QueryAuditLogsPager pager) throws StreamException {
50+
// Validate filters before making the API call
51+
if (filters == null) {
52+
throw new StreamException("Filters cannot be null for audit logs queries");
53+
}
54+
55+
filters.validate();
56+
57+
final Token token = buildAuditLogsToken(secret, TokenAction.READ);
58+
59+
RequestOption[] options = buildRequestOptions(filters, pager);
60+
return stream.queryAuditLogs(token, options);
61+
}
62+
63+
/**
64+
* Builds request options from filters and pagination settings.
65+
*
66+
* @param filters Filters to apply to the query
67+
* @param pager Pagination settings
68+
* @return Array of RequestOption for the API call
69+
*/
70+
private RequestOption[] buildRequestOptions(QueryAuditLogsFilters filters, QueryAuditLogsPager pager) {
71+
List<RequestOption> options = new ArrayList<>();
72+
73+
if (filters.getEntityType() != null && !filters.getEntityType().isEmpty() &&
74+
filters.getEntityID() != null && !filters.getEntityID().isEmpty()) {
75+
options.add(new CustomQueryParameter("entity_type", filters.getEntityType()));
76+
options.add(new CustomQueryParameter("entity_id", filters.getEntityID()));
77+
}
78+
79+
if (filters.getUserID() != null && !filters.getUserID().isEmpty()) {
80+
options.add(new CustomQueryParameter("user_id", filters.getUserID()));
81+
}
82+
83+
if (pager != null) {
84+
if (pager.getNext() != null && !pager.getNext().isEmpty()) {
85+
options.add(new CustomQueryParameter("next", pager.getNext()));
86+
}
87+
88+
if (pager.getPrev() != null && !pager.getPrev().isEmpty()) {
89+
options.add(new CustomQueryParameter("prev", pager.getPrev()));
90+
}
91+
92+
if (pager.getLimit() > 0) {
93+
options.add(new CustomQueryParameter("limit", Integer.toString(pager.getLimit())));
94+
}
95+
}
96+
97+
return options.toArray(new RequestOption[0]);
98+
}
99+
}

src/main/java/io/getstream/client/Client.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ public ModerationClient moderation() {
256256
return new ModerationClient(secret, stream.moderation());
257257
}
258258

259+
public AuditLogsClient auditLogs() {
260+
return new AuditLogsClient(secret, stream);
261+
}
262+
259263
public FileStorageClient files() {
260264
return new FileStorageClient(secret, stream.files());
261265
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package io.getstream.client;
2+
3+
import io.getstream.core.exceptions.StreamException;
4+
5+
/**
6+
* Filters for querying audit logs.
7+
* Either entityType+entityID pair OR userID is required by the API.
8+
*/
9+
public class QueryAuditLogsFilters {
10+
private String entityType;
11+
private String entityID;
12+
private String userID;
13+
14+
/**
15+
* Default constructor.
16+
* Note: You must set either (entityType AND entityID) OR userID before using.
17+
*/
18+
public QueryAuditLogsFilters() {
19+
}
20+
21+
/**
22+
* Constructor with entity type and ID.
23+
*
24+
* @param entityType The type of entity (e.g., "user", "feed")
25+
* @param entityID The ID of the entity
26+
*/
27+
public QueryAuditLogsFilters(String entityType, String entityID) {
28+
this.entityType = entityType;
29+
this.entityID = entityID;
30+
}
31+
32+
/**
33+
* Constructor with entity type, entity ID, and user ID.
34+
*
35+
* @param entityType The type of entity (e.g., "user", "feed")
36+
* @param entityID The ID of the entity
37+
* @param userID The ID of the user
38+
*/
39+
public QueryAuditLogsFilters(String entityType, String entityID, String userID) {
40+
this.entityType = entityType;
41+
this.entityID = entityID;
42+
this.userID = userID;
43+
}
44+
45+
/**
46+
* Constructor with user ID only.
47+
*
48+
* @param userID The ID of the user
49+
*/
50+
public QueryAuditLogsFilters(String userID) {
51+
this.userID = userID;
52+
}
53+
54+
public String getEntityType() {
55+
return entityType;
56+
}
57+
58+
public void setEntityType(String entityType) {
59+
this.entityType = entityType;
60+
}
61+
62+
public String getEntityID() {
63+
return entityID;
64+
}
65+
66+
public void setEntityID(String entityID) {
67+
this.entityID = entityID;
68+
}
69+
70+
public String getUserID() {
71+
return userID;
72+
}
73+
74+
public void setUserID(String userID) {
75+
this.userID = userID;
76+
}
77+
78+
/**
79+
* Validates that the filters contain the required fields.
80+
* Either (entityType AND entityID) OR userID must be set.
81+
*
82+
* @throws StreamException if the required fields are not set
83+
*/
84+
public void validate() throws StreamException {
85+
boolean hasEntityFields = entityType != null && !entityType.isEmpty() &&
86+
entityID != null && !entityID.isEmpty();
87+
boolean hasUserID = userID != null && !userID.isEmpty();
88+
89+
if (!hasEntityFields && !hasUserID) {
90+
throw new StreamException("Either entityType+entityID or userID is required for audit logs queries");
91+
}
92+
}
93+
94+
/**
95+
* Checks if the filter is valid according to API requirements.
96+
*
97+
* @return true if either (entityType AND entityID) OR userID is set
98+
*/
99+
public boolean isValid() {
100+
boolean hasEntityFields = entityType != null && !entityType.isEmpty() &&
101+
entityID != null && !entityID.isEmpty();
102+
boolean hasUserID = userID != null && !userID.isEmpty();
103+
104+
return hasEntityFields || hasUserID;
105+
}
106+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.getstream.client;
2+
3+
public class QueryAuditLogsPager {
4+
private String next;
5+
private String prev;
6+
private int limit;
7+
8+
public QueryAuditLogsPager() {
9+
}
10+
11+
public QueryAuditLogsPager(int limit) {
12+
this.limit = limit;
13+
}
14+
15+
public QueryAuditLogsPager(String next, String prev, int limit) {
16+
this.next = next;
17+
this.prev = prev;
18+
this.limit = limit;
19+
}
20+
21+
public String getNext() {
22+
return next;
23+
}
24+
25+
public void setNext(String next) {
26+
this.next = next;
27+
}
28+
29+
public String getPrev() {
30+
return prev;
31+
}
32+
33+
public void setPrev(String prev) {
34+
this.prev = prev;
35+
}
36+
37+
public int getLimit() {
38+
return limit;
39+
}
40+
41+
public void setLimit(int limit) {
42+
this.limit = limit;
43+
}
44+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package io.getstream.client;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import com.google.common.base.MoreObjects;
7+
import io.getstream.core.models.AuditLog;
8+
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Objects;
12+
13+
@JsonIgnoreProperties(ignoreUnknown = true)
14+
public class QueryAuditLogsResponse {
15+
@JsonProperty("audit_logs")
16+
private List<AuditLog> auditLogs;
17+
18+
private String next;
19+
private String prev;
20+
private String duration;
21+
22+
// Default constructor
23+
public QueryAuditLogsResponse() {
24+
this.auditLogs = new ArrayList<>();
25+
}
26+
27+
// Constructor with parameters
28+
@JsonCreator
29+
public QueryAuditLogsResponse(
30+
@JsonProperty("audit_logs") List<AuditLog> auditLogs,
31+
@JsonProperty("next") String next,
32+
@JsonProperty("prev") String prev,
33+
@JsonProperty("duration") String duration) {
34+
// Initialize mandatory fields with safe defaults if they're null
35+
this.auditLogs = auditLogs != null ? auditLogs : new ArrayList<>();
36+
this.next = next;
37+
this.prev = prev;
38+
this.duration = duration != null ? duration : "";
39+
}
40+
41+
public List<AuditLog> getAuditLogs() {
42+
return auditLogs;
43+
}
44+
45+
public String getNext() {
46+
return next;
47+
}
48+
49+
public String getPrev() {
50+
return prev;
51+
}
52+
53+
public String getDuration() {
54+
return duration;
55+
}
56+
57+
@Override
58+
public boolean equals(Object o) {
59+
if (this == o) {
60+
return true;
61+
}
62+
if (o == null || getClass() != o.getClass()) {
63+
return false;
64+
}
65+
QueryAuditLogsResponse that = (QueryAuditLogsResponse) o;
66+
return Objects.equals(auditLogs, that.auditLogs) &&
67+
Objects.equals(next, that.next) &&
68+
Objects.equals(prev, that.prev) &&
69+
Objects.equals(duration, that.duration);
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
return Objects.hash(auditLogs, next, prev, duration);
75+
}
76+
77+
@Override
78+
public String toString() {
79+
return MoreObjects.toStringHelper(this)
80+
.add("auditLogs", this.auditLogs)
81+
.add("next", this.next)
82+
.add("prev", this.prev)
83+
.add("duration", this.duration)
84+
.toString();
85+
}
86+
}

src/main/java/io/getstream/core/Stream.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,4 +572,22 @@ public CompletableFuture<ExportIDsResponse> exportUserActivities(Token token, St
572572
throw new StreamException(e);
573573
}
574574
}
575+
576+
public CompletableFuture<io.getstream.client.QueryAuditLogsResponse> queryAuditLogs(Token token, RequestOption... options) throws StreamException {
577+
try {
578+
final URL url = buildAuditLogsURL(baseURL);
579+
return httpClient
580+
.execute(buildGet(url, key, token, options))
581+
.thenApply(
582+
response -> {
583+
try {
584+
return deserialize(response, io.getstream.client.QueryAuditLogsResponse.class);
585+
} catch (StreamException | IOException e) {
586+
throw new CompletionException(e);
587+
}
588+
});
589+
} catch (MalformedURLException | URISyntaxException e) {
590+
throw new StreamException(e);
591+
}
592+
}
575593
}

0 commit comments

Comments
 (0)