Skip to content

Commit

Permalink
Subaccounts API (#462)
Browse files Browse the repository at this point in the history
* Adde Subaccounts classes

* Tidied up classes

* Added transfer filter

* Added Subaccounts exception

* Added tests

* NumberTransfer validation

* Improved coverage

* Hydrate transfers

* Hydration test

* Made secret optional

* Ignore BugRepro

* Set default start_date for transfers

* Refactored MoneyTransfer

* Truncate filter time to seconds

* 100% coverage

* Simplified transfers

* Support single subaccount filter

* Visibility of AbstractTransfer

* Added error codes to SubaccountsClient

* Guard against empty update request

* Added subaccount samples to README

* Added masterAccountId to transfer responses

* Added release date for 7.5.0
  • Loading branch information
SMadani authored Jun 14, 2023
1 parent db1a4fc commit a62a35d
Show file tree
Hide file tree
Showing 50 changed files with 3,546 additions and 38 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
src/test/java/com/vonage/client/BugRepro.java
*.groovy
.classpath
.gradle
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

# [7.5.0] - 2023-06
# [7.5.0] - 2023-06-14
- Added Subaccounts API implementation
- Added custom PIN functionality to Verify v1
- Fixed Silent Auth action URL webhook deserialization issue

Expand Down
92 changes: 72 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,57 @@ Get information about a specific secret associated with your account id:
SecretResponse response = client.getAccountClient().getSecret(API_KEY, SECRET_ID);
```

### Create a Subaccount

Create a subaccount for separate usage tracking:
```java
Account subaccount = client.getSubaccountsClient().createSubaccount(
CreateSubaccountRequest.builder().name("Department A").build());
```

### Suspend a Subaccount

Deactivate a subaccount:
```java
Account updated = client.getSubaccountsClient().updateSubaccount(
UpdateSubaccountRequest.builder(SUB_API_KEY).suspended(true).build());
```

### List subaccounts

```java
List<Account> subaccounts = client.getSubaccountsClient().listSubaccounts().getSubaccounts();
```
### Transfer a number between your accounts

Change the (sub)account a number is associated with.
```java
NumberTransfer transfer = NumberTransfer.builder()
.from(SOURCE_API_KEY).to(TARGET_API_KEY)
.number(NUMBER).country(COUNTRY_CODE).build();
client.getSubaccountsClient().transferNumber(transfer);
```

### Transfer balance to a subaccount

If a subaccount does not share its balance with the primary account, you can transfer funds to it like so:
```java
MoneyTransfer transfer = MoneyTransfer.builder()
.from(VONAGE_API_KEY).to(SUB_API_KEY)
.amount(BigDecimal.valueOf(12.34))
.reference("Top up").build();
client.getSubaccountsClient().transferBalance(transfer);
```

### List balance transfers to a subaccount since last week

```java
ListTransfersFilter filter = ListTransfersFilter.builder()
.startDate(Instant.now().minus(Duration.ofDays(7)))
.subaccount(SUB_API_KEY).build();
List<MoneyTransfer> balanceTransfers = client.getSubaccountsClient().listBalanceTransfers(filter);
```

### Video API

The Vonage Video API (formerly OpenTok) is currently in beta. You can try it out by using a beta version.
Expand Down Expand Up @@ -483,26 +534,27 @@ A: Currently no, but it is on the roadmap.

The following is a list of Vonage APIs and whether the Java SDK provides support for them:

| API | API Release Status | Supported? |
|--------------------|:--------------------:|:----------:|
| Account | General Availability ||
| Alerts | General Availability ||
| Application | General Availability ||
| Audit | Beta ||
| Conversation | Beta ||
| Dispatch | Beta ||
| External Accounts | Beta ||
| Media | Beta ||
| Messages | General Availability ||
| Number Insight | General Availability ||
| Number Management | General Availability ||
| Pricing | General Availability ||
| Redact | Developer Preview ||
| Reports | Beta ||
| SMS | General Availability ||
| Verify | General Availability ||
| Voice | General Availability ||
| Video | Beta | ☑️ |
| API | API Release Status | Supported? |
|-------------------|:--------------------:|:----------:|
| Account | General Availability ||
| Alerts | General Availability ||
| Application | General Availability ||
| Audit | Beta ||
| Conversation | Beta ||
| Dispatch | Beta ||
| External Accounts | Beta ||
| Media | Beta ||
| Messages | General Availability ||
| Number Insight | General Availability ||
| Number Management | General Availability ||
| Pricing | General Availability ||
| Redact | Developer Preview ||
| Reports | Beta ||
| SMS | General Availability ||
| Subaccounts | General Availability ||
| Verify | General Availability ||
| Voice | General Availability ||
| Video | Beta | ☑️ |


## License
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/vonage/client/AbstractMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
package com.vonage.client;

import com.vonage.client.auth.AuthMethod;
import com.vonage.client.auth.JWTAuthMethod;
import com.vonage.client.auth.SignatureAuthMethod;
import com.vonage.client.auth.TokenAuthMethod;
import com.vonage.client.logging.LoggingUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
Expand Down Expand Up @@ -133,6 +136,37 @@ protected AuthMethod getAuthMethod(Class<?>[] acceptableAuthMethods) throws Vona
return httpWrapper.getAuthCollection().getAcceptableAuthMethod(acceptable);
}

/**
* Call {@linkplain #getAuthMethod(Class[])} with {@linkplain #getAcceptableAuthMethods()}.
*
* @return An AuthMethod created from the accepted auth methods.
* @throws VonageUnexpectedException If no AuthMethod is available.
*/
protected AuthMethod getAuthMethod() throws VonageUnexpectedException {
return getAuthMethod(getAcceptableAuthMethods());
}

/**
* Utility method for obtaining the Application ID or API key from the auth method.
*
* @return The Application ID or API key.
* @throws VonageUnexpectedException If no AuthMethod is available.
* @throws IllegalStateException If the AuthMethod does not have an Application ID or API key.
*/
protected String getApplicationIdOrApiKey() throws VonageUnexpectedException {
AuthMethod am = getAuthMethod();
if (am instanceof JWTAuthMethod) {
return ((JWTAuthMethod) am).getApplicationId();
}
if (am instanceof TokenAuthMethod) {
return ((TokenAuthMethod) am).getApiKey();
}
if (am instanceof SignatureAuthMethod) {
return ((SignatureAuthMethod) am).getApiKey();
}
throw new IllegalStateException(am.getClass().getSimpleName() + " does not have API key.");
}

public void setHttpClient(HttpClient client) {
httpWrapper.setHttpClient(client);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ public abstract class VonageApiResponseException extends VonageClientException {
protected String title, detail, instance;
@JsonIgnore protected int statusCode;

protected VonageApiResponseException() {
}

/**
* Link to the <a href=https://developer.vonage.com/en/api-errors>API error type</a>.
*
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/com/vonage/client/VonageClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.vonage.client.redact.RedactClient;
import com.vonage.client.sms.SmsClient;
import com.vonage.client.sns.SnsClient;
import com.vonage.client.subaccounts.SubaccountsClient;
import com.vonage.client.verify.VerifyClient;
import com.vonage.client.verify2.Verify2Client;
import com.vonage.client.voice.VoiceClient;
Expand Down Expand Up @@ -57,7 +58,8 @@ public class VonageClient {
private final ConversionClient conversion;
private final RedactClient redact;
private final MessagesClient messages;
private final Verify2Client verify2Client;
private final Verify2Client verify2;
private final SubaccountsClient subaccounts;

private VonageClient(Builder builder) {
httpWrapper = new HttpWrapper(builder.httpConfig, builder.authCollection);
Expand All @@ -74,7 +76,8 @@ private VonageClient(Builder builder) {
conversion = new ConversionClient(httpWrapper);
redact = new RedactClient(httpWrapper);
messages = new MessagesClient(httpWrapper);
verify2Client = new Verify2Client(httpWrapper);
verify2 = new Verify2Client(httpWrapper);
subaccounts = new SubaccountsClient(httpWrapper);
}

public AccountClient getAccountClient() {
Expand Down Expand Up @@ -138,7 +141,17 @@ public MessagesClient getMessagesClient() {
* @since 7.3.0
*/
public Verify2Client getVerify2Client() {
return verify2Client;
return verify2;
}

/**
*
*
* @return The Subaccounts client.
* @since 7.5.0
*/
public SubaccountsClient getSubaccountsClient() {
return subaccounts;
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/com/vonage/client/auth/JWTAuthMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@
public class JWTAuthMethod implements AuthMethod {
private static final int SORT_KEY = 10;
private final Jwt jwt;
private final String applicationId;

public JWTAuthMethod(final String applicationId, final byte[] privateKey) {
jwt = Jwt.builder().applicationId(applicationId).privateKeyContents(new String(privateKey)).build();
jwt = Jwt.builder()
.applicationId(this.applicationId = applicationId)
.privateKeyContents(new String(privateKey)).build();
}

public JWTAuthMethod(String applicationId, Path path) throws IOException {
Expand All @@ -37,6 +40,10 @@ public String generateToken() {
return jwt.generate();
}

public String getApplicationId() {
return applicationId;
}

@Override
public RequestBuilder apply(RequestBuilder request) {
String token = jwt.generate();
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/vonage/client/auth/SignatureAuthMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ public SignatureAuthMethod(String apiKey, String apiSecret, HashUtil.HashType ha
this.hashType = hashType;
}

public String getApiKey() {
return apiKey;
}

@Override
public RequestBuilder apply(RequestBuilder request) {
request.addParameter("api_key", apiKey);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/vonage/client/auth/TokenAuthMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public TokenAuthMethod(String apiKey, String apiSecret) {
this.apiSecret = apiSecret;
}

public String getApiKey() {
return apiKey;
}

@Override
public RequestBuilder apply(RequestBuilder request) {
return request.addParameter("api_key", apiKey).addParameter("api_secret", apiSecret);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vonage.client.messages.internal;
package com.vonage.client.common;

import com.fasterxml.jackson.annotation.JsonValue;
import java.util.Objects;
import java.util.regex.Pattern;

/**
* Convenience class for validating and sanitising phone numbers to be
* compliant with the E164 format.
* Utility class for validating and sanitising phone numbers to be compliant with the E164 format.
*/
public class E164 {
static final Pattern PATTERN = Pattern.compile("[1-9]\\d{6,14}");
Expand All @@ -41,6 +41,7 @@ public E164(String number) {
}
}

@JsonValue
@Override
public String toString() {
return number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vonage.client.VonageUnexpectedException;
import com.vonage.client.messages.internal.E164;
import com.vonage.client.common.E164;
import java.util.Objects;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.vonage.client.messages;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.vonage.client.VonageApiResponseException;
Expand All @@ -40,6 +41,7 @@ void setStatusCode(int statusCode) {
* @param json The JSON string to parse.
* @return An instance of this class with all known fields populated from the JSON payload, if present.
*/
@JsonCreator
public static MessageResponseException fromJson(String json) {
return fromJson(MessageResponseException.class, json);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import com.vonage.client.messages.MessageRequest;
import com.vonage.client.messages.Channel;
import com.vonage.client.messages.MessageType;
import com.vonage.client.messages.internal.E164;
import com.vonage.client.common.E164;

@JsonInclude(value = JsonInclude.Include.NON_NULL)
public abstract class ViberRequest extends MessageRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import com.vonage.client.messages.Channel;
import com.vonage.client.messages.MessageRequest;
import com.vonage.client.messages.MessageType;
import com.vonage.client.messages.internal.E164;
import com.vonage.client.common.E164;

@JsonInclude(value = JsonInclude.Include.NON_NULL)
public abstract class WhatsappRequest extends MessageRequest {
Expand Down
Loading

0 comments on commit a62a35d

Please sign in to comment.