Skip to content

Commit f9ca2c5

Browse files
authored
Fix #525 by correcting the structure of details in LogsResponse (Audit Logs API) (#526)
* Fix #525 by correcting the structure of details in LogsResponse * Make attachin raw body optional * Adjust error messages
1 parent 0c6ca8c commit f9ca2c5

File tree

15 files changed

+656
-23
lines changed

15 files changed

+656
-23
lines changed

json-logs/samples/audit/v1/logs.json

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,51 @@
2020
"name": "",
2121
"email": "",
2222
"team": "E00000000"
23+
},
24+
"usergroup": {
25+
"id": "S00000000",
26+
"name": ""
27+
},
28+
"channel": {
29+
"id": "C00000000",
30+
"privacy": "",
31+
"name": "",
32+
"is_shared": false,
33+
"is_org_shared": false,
34+
"teams_shared_with": [
35+
"T00000000",
36+
""
37+
]
38+
},
39+
"enterprise": {
40+
"id": "E00000000",
41+
"name": "",
42+
"domain": ""
43+
},
44+
"workspace": {
45+
"id": "T00000000",
46+
"name": "",
47+
"domain": ""
48+
},
49+
"file": {
50+
"id": "F00000000",
51+
"name": "",
52+
"filetype": "",
53+
"title": ""
54+
},
55+
"app": {
56+
"id": "A00000000",
57+
"name": "",
58+
"is_distributed": false,
59+
"is_directory_approved": false,
60+
"is_workflow_app": false,
61+
"scopes": [
62+
""
63+
]
64+
},
65+
"workflow": {
66+
"id": "12345",
67+
"name": ""
2368
}
2469
},
2570
"context": {
@@ -31,6 +76,73 @@
3176
},
3277
"ua": "",
3378
"ip_address": ""
79+
},
80+
"details": {
81+
"channels": [
82+
"C00000000"
83+
],
84+
"type": "",
85+
"is_workflow": false,
86+
"shared_to": "T00000000",
87+
"reason": "",
88+
"mobile_only": false,
89+
"web_only": false,
90+
"expires_on": 12345,
91+
"name": "",
92+
"kicker": {
93+
"id": "W00000000",
94+
"name": "",
95+
"email": "",
96+
"team": "T00000000",
97+
"type": "",
98+
"user": {
99+
"id": "W00000000",
100+
"name": "",
101+
"email": "",
102+
"team": "E00000000"
103+
}
104+
},
105+
"inviter": {
106+
"id": "W00000000",
107+
"name": "",
108+
"email": "",
109+
"team": "E00000000",
110+
"type": "",
111+
"user": {
112+
"id": "W00000000",
113+
"name": "",
114+
"email": "",
115+
"team": "E00000000"
116+
}
117+
},
118+
"is_internal_integration": false,
119+
"app_owner_id": "W00000000",
120+
"bot_scopes": [
121+
""
122+
],
123+
"new_version_id": "12345",
124+
"trigger": "",
125+
"new_scopes": [
126+
""
127+
],
128+
"previous_scopes": [
129+
""
130+
],
131+
"granular_bot_token": false,
132+
"origin_team": "T00000000",
133+
"target_team": "T00000000",
134+
"scopes": [
135+
""
136+
],
137+
"resolution": "",
138+
"app_previously_resolved": false,
139+
"admin_app_id": "",
140+
"new_value": [
141+
"C00000000"
142+
],
143+
"previous_value": [
144+
""
145+
]
34146
}
35147
}
36148
],

slack-api-client/src/main/java/com/slack/api/audit/AuditApiErrorResponse.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@Data
77
@EqualsAndHashCode(callSuper = false)
88
public class AuditApiErrorResponse implements AuditApiResponse {
9+
private transient String rawBody;
910

1011
private boolean ok;
1112
private String warning;

slack-api-client/src/main/java/com/slack/api/audit/AuditApiException.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ private static AuditApiErrorResponse parse(SlackConfig config, String responseBo
4444
AuditApiErrorResponse parsedErrorResponse = null;
4545
try {
4646
parsedErrorResponse = GsonFactory.createSnakeCase(config).fromJson(responseBody, AuditApiErrorResponse.class);
47+
parsedErrorResponse.setRawBody(responseBody);
4748
} catch (Exception e) {
4849
if (log.isDebugEnabled()) {
4950
String responseToPrint = responseBody.length() > 1000 ? responseBody.subSequence(0, 1000) + " ..." : responseBody;

slack-api-client/src/main/java/com/slack/api/audit/AuditApiResponse.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@ public interface AuditApiResponse {
2424

2525
void setProvided(String provided);
2626

27+
String getRawBody();
28+
29+
void setRawBody(String rawBody);
2730
}

slack-api-client/src/main/java/com/slack/api/audit/AuditClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public interface AuditClient {
2525

2626
String ENDPOINT_URL_PREFIX = "https://api.slack.com/audit/v1/";
2727

28+
AuditClient attachRawBody(boolean attachRawBody);
29+
2830
SchemasResponse getSchemas() throws IOException, AuditApiException;
2931

3032
SchemasResponse getSchemas(SchemasRequest req) throws IOException, AuditApiException;

slack-api-client/src/main/java/com/slack/api/audit/impl/AuditClientImpl.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.slack.api.RequestConfigurator;
44
import com.slack.api.audit.AuditApiException;
55
import com.slack.api.audit.AuditApiRequest;
6+
import com.slack.api.audit.AuditApiResponse;
67
import com.slack.api.audit.AuditClient;
78
import com.slack.api.audit.request.ActionsRequest;
89
import com.slack.api.audit.request.LogsRequest;
@@ -24,6 +25,7 @@ public class AuditClientImpl implements AuditClient {
2425

2526
private final SlackHttpClient slackHttpClient;
2627
private final String token;
28+
private boolean attachRawBody = false;
2729

2830
public AuditClientImpl(SlackHttpClient slackHttpClient) {
2931
this(slackHttpClient, null);
@@ -42,6 +44,12 @@ public void setEndpointUrlPrefix(String endpointUrlPrefix) {
4244
this.endpointUrlPrefix = endpointUrlPrefix;
4345
}
4446

47+
@Override
48+
public AuditClient attachRawBody(boolean attachRawBody) {
49+
this.attachRawBody = attachRawBody;
50+
return this;
51+
}
52+
4553
@Override
4654
public SchemasResponse getSchemas() throws IOException, AuditApiException {
4755
return getSchemas(SchemasRequest.builder().build());
@@ -114,16 +122,20 @@ private String getToken(AuditApiRequest req) {
114122
}
115123
}
116124

117-
private <T> T doGet(String url, Map<String, String> query, String token, Class<T> clazz) throws IOException, AuditApiException {
125+
private <T extends AuditApiResponse> T doGet(String url, Map<String, String> query, String token, Class<T> clazz) throws IOException, AuditApiException {
118126
Response response = slackHttpClient.get(url, query, token);
119127
return parseJsonResponseAndRunListeners(response, clazz);
120128
}
121129

122-
private <T> T parseJsonResponseAndRunListeners(Response response, Class<T> clazz) throws IOException, AuditApiException {
130+
private <T extends AuditApiResponse> T parseJsonResponseAndRunListeners(Response response, Class<T> clazz) throws IOException, AuditApiException {
123131
String body = response.body().string();
124132
slackHttpClient.runHttpResponseListeners(response, body);
125133
if (response.isSuccessful()) {
126-
return GsonFactory.createSnakeCase(slackHttpClient.getConfig()).fromJson(body, clazz);
134+
T apiResponse = GsonFactory.createSnakeCase(slackHttpClient.getConfig()).fromJson(body, clazz);
135+
if (attachRawBody) {
136+
apiResponse.setRawBody(body);
137+
}
138+
return apiResponse;
127139
} else {
128140
throw new AuditApiException(slackHttpClient.getConfig(), response, body);
129141
}

slack-api-client/src/main/java/com/slack/api/audit/response/ActionsResponse.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
@Data
1010
@EqualsAndHashCode(callSuper = false)
1111
public class ActionsResponse implements AuditApiResponse {
12+
private transient String rawBody;
13+
1214
private boolean ok;
1315
private String warning;
1416
private String error;

slack-api-client/src/main/java/com/slack/api/audit/response/LogsResponse.java

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
import lombok.EqualsAndHashCode;
88

99
import java.util.List;
10+
import java.util.Map;
1011

1112
@Data
1213
@EqualsAndHashCode(callSuper = false)
1314
public class LogsResponse implements AuditApiResponse {
15+
private transient String rawBody;
16+
1417
private boolean ok;
1518
private String warning;
1619
private String error;
@@ -56,6 +59,7 @@ public static class Entity {
5659
private Enterprise enterprise;
5760
private File file;
5861
private Channel channel;
62+
private Workflow workflow;
5963
}
6064

6165
@Data
@@ -66,6 +70,8 @@ public static class App {
6670
private Boolean distributed;
6771
@SerializedName("is_directory_approved")
6872
private Boolean directoryApproved;
73+
@SerializedName("is_workflow_app")
74+
private Boolean workflowApp;
6975
private List<String> scopes;
7076
}
7177

@@ -109,6 +115,12 @@ public static class Channel {
109115
private List<String> teamsSharedWith;
110116
}
111117

118+
@Data
119+
public static class Workflow {
120+
private String id;
121+
private String name;
122+
}
123+
112124
@Data
113125
public static class Context {
114126
private Location location;
@@ -124,40 +136,78 @@ public static class Location {
124136
private String domain;
125137
}
126138

139+
/**
140+
* The data structure for new_value, previous_value is greatly flexible.
141+
* This class supports multiple patterns for those.
142+
*/
143+
@Data
144+
public static class DetailsChangedValue {
145+
// e.g., ["C111", "C222"]
146+
private List<String> stringValues;
147+
// e.g., {"type": ["TOPLEVEL_ADMINS_AND_OWNERS_AND_SELECTED"]}
148+
private Map<String, List<String>> namedStringValues;
149+
}
150+
127151
@Data
128152
public static class Details {
129-
@SerializedName("is_internal_integration")
130-
private Boolean internalIntegration;
153+
private String type;
131154
private String appOwnerId;
155+
private List<String> scopes; // app_scopes_expanded
156+
private List<String> botScopes;
132157
private List<String> newScopes;
133158
private List<String> previousScopes;
134-
private String type;
135159
private Inviter inviter;
136-
private String newValue;
137-
private String previousValue;
160+
private DetailsChangedValue newValue; // pref.who_can_manage_shared_channels etc
161+
private DetailsChangedValue previousValue; // pref.who_can_manage_shared_channels etc
138162
private Kicker kicker;
139163
private String installerUserId;
140164
private Boolean appPreviouslyApproved;
141165
private List<String> oldScopes;
142166
private String name;
143167
private String botId;
168+
private List<String> channels;
144169
private List<Permission> permissions;
170+
private String sharedTo; // channel_workspaces_updated
171+
private String reason;
172+
@SerializedName("is_internal_integration")
173+
private Boolean internalIntegration;
174+
@SerializedName("is_workflow")
175+
private Boolean workflow; // user_channel_join
176+
private Boolean mobileOnly; // user_session_reset_by_admin
177+
private Boolean webOnly; // user_session_reset_by_admin
178+
private Integer expiresOn; // guest_expiration_set
179+
private String newVersionId; // workflow_published
180+
private String trigger; // workflow_published
181+
private Boolean granularBotToken; // app_scopes_expanded
182+
private String originTeam; // external_shared_channel_invite_approved
183+
private String targetTeam; // external_shared_channel_invite_approved
184+
private String resolution; // app_approved
185+
private Boolean appPreviouslyResolved; // app_approved
186+
private String adminAppId; // app_approved
145187
}
146188

147189
@Data
148190
public static class Inviter {
149191
private String type;
192+
150193
private User user;
194+
151195
private String id;
152196
private String name;
153197
private String email;
198+
private String team;
154199
}
155200

156201
@Data
157202
public static class Kicker {
203+
private String type;
204+
205+
private User user;
206+
158207
private String id;
159208
private String name;
160209
private String email;
210+
private String team;
161211
}
162212

163213
@Data

slack-api-client/src/main/java/com/slack/api/audit/response/SchemasResponse.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
@Data
1010
@EqualsAndHashCode(callSuper = false)
1111
public class SchemasResponse implements AuditApiResponse {
12+
private transient String rawBody;
13+
1214
private boolean ok;
1315
private String warning;
1416
private String error;

0 commit comments

Comments
 (0)