Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Check out our [official Java SDK documentation](https://docs.authsignal.com/sdks
Add this dependency to your project's build file:

```groovy
implementation 'com.authsignal:authsignal-java:2.7.0'
implementation 'com.authsignal:authsignal-java:2.9.0'
```

### Maven users
Expand All @@ -26,7 +26,7 @@ Add this dependency to your project's POM:
<dependency>
<groupId>com.authsignal</groupId>
<artifactId>authsignal-java</artifactId>
<version>2.7.0</version>
<version>2.9.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=2.8.0
version=2.9.0
60 changes: 59 additions & 1 deletion src/main/java/com/authsignal/AuthsignalClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class AuthsignalClient {

private static final String DEFAULT_API_URL = "https://api.authsignal.com/v1";
private static final int DEFAULT_RETRIES = 2;
private static final String VERSION = "2.6.0";
private static final String VERSION = "2.9.0";

public Webhook webhook;

Expand Down Expand Up @@ -58,6 +58,41 @@ public CompletableFuture<GetUserResponse> getUser(GetUserRequest request) {
response -> new Gson().fromJson(response.body(), GetUserResponse.class));
}

public CompletableFuture<QueryUsersResponse> queryUsers(QueryUsersRequest request) {
Map<String, String> params = new HashMap<>();

if (request.username != null) {
params.put("username", request.username);
}

if (request.email != null) {
params.put("email", request.email);
}

if (request.phoneNumber != null) {
params.put("phoneNumber", request.phoneNumber);
}

if (request.token != null) {
params.put("token", request.token);
}

if (request.limit != null) {
params.put("limit", request.limit.toString());
}

if (request.lastEvaluatedUserId != null) {
params.put("lastEvaluatedUserId", request.lastEvaluatedUserId);
}

String query = buildQueryString(params);

String path = "/users" + (query.isEmpty() ? "" : "?" + query);

return getRequest(path)
.thenApply(response -> new Gson().fromJson(response.body(), QueryUsersResponse.class));
}

public CompletableFuture<UserAttributes> updateUser(UpdateUserRequest request) {
String path = String.format("/users/%s", request.userId);

Expand Down Expand Up @@ -112,6 +147,29 @@ public CompletableFuture<GetActionResponse> getAction(GetActionRequest request)
return getRequest(path).thenApply(response -> new Gson().fromJson(response.body(), GetActionResponse.class));
}

public CompletableFuture<QueryUserActionsResponseItem[]> queryUserActions(QueryUserActionsRequest request) {
Map<String, String> params = new HashMap<>();

if (request.fromDate != null) {
params.put("fromDate", request.fromDate);
}

if (request.actionCodes != null && request.actionCodes.length > 0) {
params.put("codes", String.join(",", request.actionCodes));
}

if (request.state != null) {
params.put("state", request.state.toString());
}

String query = buildQueryString(params);

String path = String.format("/users/%s/actions", request.userId) + (query.isEmpty() ? "" : "?" + query);

return getRequest(path)
.thenApply(response -> new Gson().fromJson(response.body(), QueryUserActionsResponseItem[].class));
}

public CompletableFuture<ActionAttributes> updateAction(UpdateActionRequest request) {
String path = String.format("/users/%s/actions/%s/%s", request.userId, request.action, request.idempotencyKey);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.authsignal.model;

public class QueryUserActionsRequest extends ApiModel {
public String userId;
public String fromDate;
public String[] actionCodes;
public UserActionState state;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.authsignal.model;

public class QueryUserActionsResponseItem extends ApiModel {
public String actionCode;
public String idempotencyKey;
public String createdAt;
public String updatedAt;
public String state;
public String stateUpdatedAt;
public VerificationMethodType verificationMethod;
public String verifiedByAuthenticatorId;
}

11 changes: 11 additions & 0 deletions src/main/java/com/authsignal/model/QueryUsersRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.authsignal.model;

public class QueryUsersRequest extends ApiModel {
public String username;
public String email;
public String phoneNumber;
public String token;
public Integer limit;
public String lastEvaluatedUserId;
}

8 changes: 8 additions & 0 deletions src/main/java/com/authsignal/model/QueryUsersResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.authsignal.model;

public class QueryUsersResponse extends ApiModel {
public QueryUsersResponseUser[] users;
public String lastEvaluatedUserId;
public Object tokenPayload;
}

11 changes: 11 additions & 0 deletions src/main/java/com/authsignal/model/QueryUsersResponseUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.authsignal.model;

public class QueryUsersResponseUser extends ApiModel {
public String userId;
public String email;
public Boolean emailVerified;
public String phoneNumber;
public Boolean phoneNumberVerified;
public String username;
}

127 changes: 127 additions & 0 deletions src/test/java/com/authsignal/AuthsignalClientTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,131 @@ public void testPasskeyAuthenticator() {
fail("should not throw any exception");
}
}

@Test
public void testQueryUsers() {
String userId = UUID.randomUUID().toString();
String testEmail = "test-" + UUID.randomUUID().toString() + "@example.com";

try {
// First create a user with a unique email
UpdateUserRequest updateUserRequest = new UpdateUserRequest();
updateUserRequest.userId = userId;
updateUserRequest.attributes = new UserAttributes();
updateUserRequest.attributes.email = testEmail;

client.updateUser(updateUserRequest).get();

// Query by email
QueryUsersRequest queryRequest = new QueryUsersRequest();
queryRequest.email = testEmail;

QueryUsersResponse queryResponse = client.queryUsers(queryRequest).get();

assertNotNull("queryResponse should exist", queryResponse);
assertNotNull("users array should exist", queryResponse.users);
assertTrue("users array should not be empty", queryResponse.users.length > 0);

QueryUsersResponseUser user = queryResponse.users[0];
assertEquals("userId should match", userId, user.userId);
assertEquals("email should match", testEmail, user.email);

// Test pagination with limit
QueryUsersRequest limitRequest = new QueryUsersRequest();
limitRequest.email = testEmail;
limitRequest.limit = 1;

QueryUsersResponse limitResponse = client.queryUsers(limitRequest).get();

assertNotNull("limitResponse should exist", limitResponse);
assertTrue("users array should respect limit", limitResponse.users.length <= 1);

// Clean up
DeleteUserRequest deleteRequest = new DeleteUserRequest();
deleteRequest.userId = userId;
client.deleteUser(deleteRequest).get();
} catch (Exception e) {
System.out.println(e.getMessage());

fail("should not throw any exception");
}
}

@Test
public void testQueryUserActions() {
String userId = UUID.randomUUID().toString();

EnrollVerifiedAuthenticatorRequest enrollRequest = new EnrollVerifiedAuthenticatorRequest();
enrollRequest.userId = userId;
enrollRequest.attributes = new EnrollVerifiedAuthenticatorAttributes();
enrollRequest.attributes.verificationMethod = VerificationMethodType.SMS;
enrollRequest.attributes.phoneNumber = "+6427000000";

try {
EnrollVerifiedAuthenticatorResponse enrollResponse = client.enrollVerifiedAuthenticator(enrollRequest)
.get();

assertNotNull("enrollResponse should exist", enrollResponse);

// Track an action
TrackRequest trackRequest = new TrackRequest();
trackRequest.userId = userId;
trackRequest.action = "signIn";

TrackResponse trackResponse = client.track(trackRequest).get();

assertNotNull("trackResponse should exist", trackResponse);

// Query user actions
QueryUserActionsRequest queryRequest = new QueryUserActionsRequest();
queryRequest.userId = userId;

QueryUserActionsResponseItem[] actions = client.queryUserActions(queryRequest).get();

assertNotNull("actions should exist", actions);
assertTrue("actions should not be empty", actions.length > 0);

QueryUserActionsResponseItem action = actions[0];
assertEquals("action code should match", "signIn", action.actionCode);
assertNotNull("idempotencyKey should exist", action.idempotencyKey);
assertNotNull("createdAt should exist", action.createdAt);
assertNotNull("state should exist", action.state);

// Query with filter by action code
QueryUserActionsRequest filteredRequest = new QueryUserActionsRequest();
filteredRequest.userId = userId;
filteredRequest.actionCodes = new String[] { "signIn" };

QueryUserActionsResponseItem[] filteredActions = client.queryUserActions(filteredRequest).get();

assertNotNull("filtered actions should exist", filteredActions);
assertTrue("filtered actions should not be empty", filteredActions.length > 0);

for (QueryUserActionsResponseItem filteredAction : filteredActions) {
assertEquals("all actions should have signIn code", "signIn", filteredAction.actionCode);
}

// Query with state filter
QueryUserActionsRequest stateRequest = new QueryUserActionsRequest();
stateRequest.userId = userId;
stateRequest.state = UserActionState.CHALLENGE_REQUIRED;

QueryUserActionsResponseItem[] stateActions = client.queryUserActions(stateRequest).get();

assertNotNull("state filtered actions should exist", stateActions);
// Actions exist if any match the state filter
if (stateActions.length > 0) {
assertEquals("action state should match filter", "CHALLENGE_REQUIRED", stateActions[0].state);
}

// Clean up
DeleteUserRequest deleteRequest = new DeleteUserRequest();
deleteRequest.userId = userId;
client.deleteUser(deleteRequest).get();
} catch (Exception e) {
System.out.println(e.getMessage());

fail("should not throw any exception");
}
}
}
Loading