From 128575388e98f385e417002f251c25042d2c6e1b Mon Sep 17 00:00:00 2001 From: sbansla Date: Wed, 12 Nov 2025 17:08:07 +0530 Subject: [PATCH] added methods for reading metadata for read and create for testing --- src/main/java/com/twilio/base/CreatorV1.java | 67 +++++++ .../java/com/twilio/base/PageMetadata.java | 117 ++++++++++++ src/main/java/com/twilio/base/ReaderV1.java | 175 ++++++++++++++++++ .../java/com/twilio/base/ResourceSet.java | 29 ++- .../com/twilio/base/ResponseMetadata.java | 79 ++++++++ src/main/java/com/twilio/http/Response.java | 19 ++ .../api/v2010/account/MessageCreator.java | 55 +++++- .../rest/api/v2010/account/MessageReader.java | 69 ++++++- 8 files changed, 603 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/twilio/base/CreatorV1.java create mode 100644 src/main/java/com/twilio/base/PageMetadata.java create mode 100644 src/main/java/com/twilio/base/ReaderV1.java create mode 100644 src/main/java/com/twilio/base/ResponseMetadata.java diff --git a/src/main/java/com/twilio/base/CreatorV1.java b/src/main/java/com/twilio/base/CreatorV1.java new file mode 100644 index 0000000000..2be08c50d5 --- /dev/null +++ b/src/main/java/com/twilio/base/CreatorV1.java @@ -0,0 +1,67 @@ +package com.twilio.base; + +import com.twilio.Twilio; +import com.twilio.http.TwilioRestClient; + +import java.util.concurrent.CompletableFuture; + +/** + * Executor for creation of a resource. + * + * @param type of the resource + */ +public abstract class CreatorV1 { + + /** + * Execute an async request using default client. + * + * @return future that resolves to requested object + */ + public CompletableFuture createAsync() { + return createAsync(Twilio.getRestClient()); + } + + /** + * Execute an async request using specified client. + * + * @param client client used to make request + * @return future that resolves to requested object + */ + public CompletableFuture createAsync(final TwilioRestClient client) { + return CompletableFuture.supplyAsync(() -> create(client), Twilio.getExecutorService()); + } + + /** + * Execute a request using default client. + * + * @return Requested object + */ + public T create() { + return create(Twilio.getRestClient()); + } + + /** + * Execute a request using specified client. + * + * @param client client used to make request + * @return Requested object + */ + public abstract T create(final TwilioRestClient client); + + /** + * Execute a request and return result with response metadata (headers, status code). + * + * @return ResponseMetadata containing resource and HTTP metadata + */ + public ResponseMetadata createWithMetadata() { + return createWithMetadata(Twilio.getRestClient()); + } + + /** + * Execute a request using specified client and return result with response metadata. + * + * @param client client used to make request + * @return ResponseMetadata containing resource and HTTP metadata + */ + public abstract ResponseMetadata createWithMetadata(final TwilioRestClient client); +} diff --git a/src/main/java/com/twilio/base/PageMetadata.java b/src/main/java/com/twilio/base/PageMetadata.java new file mode 100644 index 0000000000..006b17e065 --- /dev/null +++ b/src/main/java/com/twilio/base/PageMetadata.java @@ -0,0 +1,117 @@ +package com.twilio.base; + +import com.twilio.http.TwilioRestClient; +import java.util.Iterator; +import java.util.Map; + +/** + * Wrapper around ResourceSet that includes HTTP response metadata from first page. + * Provides access to headers and status code while supporting pagination. + * + * @param type of the resource + */ +public class PageMetadata implements Iterable { + + private final ResourceSet resourceSet; + private final Map headers; + private final int statusCode; + + /** + * Create page metadata wrapper. + * + * @param resourceSet the resource set with pagination + * @param statusCode HTTP status code from first page + * @param headers HTTP headers from first page + */ + public PageMetadata( + final ResourceSet resourceSet, + final int statusCode, + final Map headers + ) { + this.resourceSet = resourceSet; + this.statusCode = statusCode; + this.headers = headers; + } + + /** + * Get HTTP headers from first page response. + * + * @return map of header name to value + */ + public Map getHeaders() { + return headers; + } + + /** + * Get HTTP status code from first page response. + * + * @return status code + */ + public int getStatusCode() { + return statusCode; + } + + /** + * Get specific header value. + * + * @param headerName name of the header + * @return header value or null if not present + */ + public String getHeader(final String headerName) { + return headers != null ? headers.get(headerName) : null; + } + + /** + * Get underlying ResourceSet for advanced operations. + * + * @return the wrapped resource set + */ + public ResourceSet getResourceSet() { + return resourceSet; + } + + public boolean isAutoPaging() { + return resourceSet.isAutoPaging(); + } + + public PageMetadata setAutoPaging(final boolean autoPaging) { + resourceSet.setAutoPaging(autoPaging); + return this; + } + + public Integer getPageSize() { + return resourceSet.getPageSize(); + } + + public PageMetadata setPageSize(final int pageSize) { + resourceSet.setPageSize(pageSize); + return this; + } + + public Long getLimit() { + return resourceSet.getLimit(); + } + + public PageMetadata setLimit(final long limit) { + resourceSet.setLimit(limit); + return this; + } + + public long getPageLimit() { + return resourceSet.getPageLimit(); + } + + @Override + public Iterator iterator() { + return resourceSet.iterator(); + } + + @Override + public String toString() { + return "PageMetadata{" + + "statusCode=" + statusCode + + ", headers=" + headers + + ", resourceSet=" + resourceSet + + '}'; + } +} diff --git a/src/main/java/com/twilio/base/ReaderV1.java b/src/main/java/com/twilio/base/ReaderV1.java new file mode 100644 index 0000000000..7545345829 --- /dev/null +++ b/src/main/java/com/twilio/base/ReaderV1.java @@ -0,0 +1,175 @@ +package com.twilio.base; + +import com.twilio.Twilio; +import com.twilio.http.TwilioRestClient; +import com.twilio.rest.api.v2010.account.Message; + +import java.util.concurrent.CompletableFuture; + +/** + * Executor for listing of a resource. + * + * @param type of the resource + */ +public abstract class ReaderV1 { + + private Integer pageSize; + private Long limit; + + /** + * Execute a request using default client. + * + * @return ResourceSet of objects + */ + public ResourceSet read() { + return read(Twilio.getRestClient()); + } + + /** + * Execute a request using specified client. + * + * @param client client used to make request + * @return ResourceSet of objects + */ + public abstract ResourceSet read(final TwilioRestClient client); + + /** + * Execute an async request using default client. + * + * @return future that resolves to the ResourceSet of objects + */ + public CompletableFuture> readAsync() { + return readAsync(Twilio.getRestClient()); + } + + /** + * Execute an async request using specified client. + * + * @param client client used to make request + * @return future that resolves to the ResourceSet of objects + */ + public CompletableFuture> readAsync(final TwilioRestClient client) { + return CompletableFuture.supplyAsync(() -> read(client), Twilio.getExecutorService()); + } + + /** + * Fetch the first page of resources. + * + * @return Page containing the first pageSize of resources + */ + public Page firstPage() { + return firstPage(Twilio.getRestClient()); + } + + /** + * Fetch the first page of resources using specified client. + * + * @param client client used to fetch + * @return Page containing the first pageSize of resources + */ + public abstract Page firstPage(final TwilioRestClient client); + + /** + * Retrieve the target page of resources. + * + * @param targetUrl API-generated URL for the requested results page + * @return Page containing the target pageSize of resources + */ + public Page getPage(final String targetUrl) { + return getPage(targetUrl, Twilio.getRestClient()); + } + + /** + * Retrieve the target page of resources. + * + * @param targetUrl API-generated URL for the requested results page + * @param client client used to fetch + * @return Page containing the target pageSize of resources + */ + public abstract Page getPage(final String targetUrl, final TwilioRestClient client); + + /** + * Fetch the following page of resources. + * + * @param page current page of resources + * @return Page containing the next pageSize of resources + */ + public Page nextPage(final Page page) { + return nextPage(page, Twilio.getRestClient()); + } + + /** + * Fetch the following page of resources using specified client. + * + * @param page current page of resources + * @param client client used to fetch + * @return Page containing the next pageSize of resources + */ + public abstract Page nextPage(final Page page, final TwilioRestClient client); + + /** + * Fetch the prior page of resources. + * + * @param page current page of resources + * @return Page containing the previous pageSize of resources + */ + public Page previousPage(final Page page) { + return previousPage(page, Twilio.getRestClient()); + } + + + /** + * Execute list request with response metadata (headers, status code). + * + * @return PageMetadata containing resource set and HTTP metadata + */ + public PageMetadata readWithMetadata() { + return readWithMetadata(Twilio.getRestClient()); + } + + /** + * Execute list request with response metadata using specified client. + * + * @param client client used to make request + * @return PageMetadata containing resource set and HTTP metadata + */ + public abstract PageMetadata readWithMetadata(final TwilioRestClient client); + + /** + * Fetch the prior page of resources using specified client. + * + * @param page current page of resources + * @param client client used to fetch + * @return Page containing the previous pageSize of resources + */ + public abstract Page previousPage(final Page page, final TwilioRestClient client); + + public Integer getPageSize() { + return pageSize; + } + + public ReaderV1 pageSize(final int pageSize) { + this.pageSize = pageSize; + return this; + } + + public Long getLimit() { + return limit; + } + + /** + * Sets the max number of records to read. + * + * @param limit max number of records to read + * @return this reader + */ + public ReaderV1 limit(final long limit) { + this.limit = limit; + + if (this.pageSize == null) { + this.pageSize = this.limit.intValue(); + } + + return this; + } +} diff --git a/src/main/java/com/twilio/base/ResourceSet.java b/src/main/java/com/twilio/base/ResourceSet.java index 6e5608adce..fa719462af 100644 --- a/src/main/java/com/twilio/base/ResourceSet.java +++ b/src/main/java/com/twilio/base/ResourceSet.java @@ -13,6 +13,7 @@ public class ResourceSet implements Iterable { private final Reader reader; + private final ReaderV1 readerV1; private final TwilioRestClient client; private final Page firstPage; // Store reference to first page to enable multiple iterations @@ -32,6 +33,7 @@ public class ResourceSet implements Iterable { */ public ResourceSet(final Reader reader, final TwilioRestClient client, final Page page) { this.reader = reader; + this.readerV1 = null; this.client = client; this.firstPage = page; // Save first page to allow resetting iterator state this.page = page; @@ -43,6 +45,20 @@ public ResourceSet(final Reader reader, final TwilioRestClient client, final } } + public ResourceSet(final ReaderV1 reader, final TwilioRestClient client, final Page page) { + this.reader = null; + this.readerV1 = reader; + this.client = client; + this.firstPage = page; // Save first page to allow resetting iterator state + this.page = page; + this.iterator = page.getRecords().iterator(); + this.autoPaging = true; + + if (readerV1.getLimit() != null) { + this.pageLimit = (long)(Math.ceil((double)readerV1.getLimit() / (double)page.getPageSize())); + } + } + public boolean isAutoPaging() { return autoPaging; } @@ -57,16 +73,19 @@ public Integer getPageSize() { } public ResourceSet setPageSize(final int pageSize) { - reader.pageSize(pageSize); + if (readerV1 != null) readerV1.pageSize(pageSize); + else reader.pageSize(pageSize); return this; } public Long getLimit() { + if (readerV1 != null) return readerV1.getLimit(); return reader.getLimit(); } public ResourceSet setLimit(final long limit) { - reader.limit(limit); + if (readerV1 != null) readerV1.limit(limit); + else reader.limit(limit); return this; } @@ -91,7 +110,11 @@ private void fetchNextPage() { } pages++; - page = reader.nextPage(page, client); + if (readerV1 != null) { + page = readerV1.nextPage(page, client); + } else { + page = reader.nextPage(page, client); + } iterator = page.getRecords().iterator(); } diff --git a/src/main/java/com/twilio/base/ResponseMetadata.java b/src/main/java/com/twilio/base/ResponseMetadata.java new file mode 100644 index 0000000000..1c216683a3 --- /dev/null +++ b/src/main/java/com/twilio/base/ResponseMetadata.java @@ -0,0 +1,79 @@ +package com.twilio.base; + +import java.util.Map; + +/** + * Wrapper containing a resource along with HTTP response metadata (headers, status code). + * Allows access to response headers while maintaining backward compatibility. + * + * @param type of the resource + */ +public class ResponseMetadata { + + private final T resource; + private final Map headers; + private final int statusCode; + + /** + * Create response metadata wrapper. + * + * @param resource the resource object (Message, Call, etc.) + * @param statusCode HTTP status code + * @param headers HTTP response headers + */ + public ResponseMetadata( + final T resource, + final int statusCode, + final Map headers + ) { + this.resource = resource; + this.statusCode = statusCode; + this.headers = headers; + } + + /** + * Get the resource. + * + * @return the resource + */ + public T getResource() { + return resource; + } + + /** + * Get HTTP response headers. + * + * @return map of header name to value + */ + public Map getHeaders() { + return headers; + } + + /** + * Get HTTP status code. + * + * @return status code + */ + public int getStatusCode() { + return statusCode; + } + + /** + * Get specific header value. + * + * @param headerName name of the header + * @return header value or null if not present + */ + public String getHeader(final String headerName) { + return headers != null ? headers.get(headerName) : null; + } + + @Override + public String toString() { + return "ResponseMetadata{" + + "statusCode=" + statusCode + + ", headers=" + headers + + ", resource=" + resource + + '}'; + } +} diff --git a/src/main/java/com/twilio/http/Response.java b/src/main/java/com/twilio/http/Response.java index 05f675ab7e..91fdff0084 100644 --- a/src/main/java/com/twilio/http/Response.java +++ b/src/main/java/com/twilio/http/Response.java @@ -6,6 +6,8 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Map; import java.util.Scanner; public class Response { @@ -117,4 +119,21 @@ public int getStatusCode() { public Header[] getHeaders() { return headers; } + + /** + * Get headers as a Map for easy access. + * + * @return map of header name to value (or null if no headers) + */ + public Map getHeadersMap() { + if (headers == null) { + return null; + } + + Map headerMap = new HashMap<>(); + for (Header header : headers) { + headerMap.put(header.getName(), header.getValue()); + } + return headerMap; + } } diff --git a/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java b/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java index 3154c8f8e4..00cf7d78b2 100644 --- a/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java +++ b/src/main/java/com/twilio/rest/api/v2010/account/MessageCreator.java @@ -15,6 +15,8 @@ package com.twilio.rest.api.v2010.account; import com.twilio.base.Creator; +import com.twilio.base.CreatorV1; +import com.twilio.base.ResponseMetadata; import com.twilio.constant.EnumConstants; import com.twilio.constant.EnumConstants.ParameterType; import com.twilio.converter.Promoter; @@ -33,7 +35,7 @@ import java.time.ZonedDateTime; import java.util.List; -public class MessageCreator extends Creator { +public class MessageCreator extends CreatorV1 { private String pathAccountSid; private com.twilio.type.PhoneNumber to; @@ -350,6 +352,57 @@ public Message create(final TwilioRestClient client) { return Message.fromJson(response.getStream(), client.getObjectMapper()); } + @Override + public ResponseMetadata createWithMetadata(final TwilioRestClient client) { + String path = "/2010-04-01/Accounts/{AccountSid}/Messages.json"; + + this.pathAccountSid = + this.pathAccountSid == null + ? client.getAccountSid() + : this.pathAccountSid; + path = + path.replace( + "{" + "AccountSid" + "}", + this.pathAccountSid.toString() + ); + + Request request = new Request( + HttpMethod.POST, + Domains.API.toString(), + path + ); + request.setContentType(EnumConstants.ContentType.FORM_URLENCODED); + addPostParams(request); + + Response response = client.request(request); + + if (response == null) { + throw new ApiConnectionException( + "Message creation failed: Unable to connect to server" + ); + } else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) { + RestException restException = RestException.fromJson( + response.getStream(), + client.getObjectMapper() + ); + if (restException == null) { + throw new ApiException( + "Server Error, no content", + response.getStatusCode() + ); + } + throw new ApiException(restException); + } + + Message message = Message.fromJson(response.getStream(), client.getObjectMapper()); + + return new ResponseMetadata<>( + message, + response.getStatusCode(), + response.getHeadersMap() + ); + } + private void addPostParams(final Request request) { if (to != null) { Serializer.toString(request, "To", to, ParameterType.URLENCODED); diff --git a/src/main/java/com/twilio/rest/api/v2010/account/MessageReader.java b/src/main/java/com/twilio/rest/api/v2010/account/MessageReader.java index 8ef4320c20..c728d95b3f 100644 --- a/src/main/java/com/twilio/rest/api/v2010/account/MessageReader.java +++ b/src/main/java/com/twilio/rest/api/v2010/account/MessageReader.java @@ -15,7 +15,8 @@ package com.twilio.rest.api.v2010.account; import com.twilio.base.Page; -import com.twilio.base.Reader; +import com.twilio.base.PageMetadata; +import com.twilio.base.ReaderV1; import com.twilio.base.ResourceSet; import com.twilio.constant.EnumConstants.ParameterType; import com.twilio.converter.Promoter; @@ -28,10 +29,10 @@ import com.twilio.http.Response; import com.twilio.http.TwilioRestClient; import com.twilio.rest.Domains; -import com.twilio.type.*; + import java.time.ZonedDateTime; -public class MessageReader extends Reader { +public class MessageReader extends ReaderV1 { private String pathAccountSid; private com.twilio.type.PhoneNumber to; @@ -113,6 +114,65 @@ public Page firstPage(final TwilioRestClient client) { return pageForRequest(client, request); } + @Override + public PageMetadata readWithMetadata(final TwilioRestClient client) { + String path = "/2010-04-01/Accounts/{AccountSid}/Messages.json"; + + this.pathAccountSid = + this.pathAccountSid == null + ? client.getAccountSid() + : this.pathAccountSid; + path = + path.replace( + "{" + "AccountSid" + "}", + this.pathAccountSid.toString() + ); + + Request request = new Request( + HttpMethod.GET, + Domains.API.toString(), + path + ); + addQueryParams(request); + + Response response = client.request(request); + + if (response == null) { + throw new ApiConnectionException( + "Message read failed: Unable to connect to server" + ); + } else if (!TwilioRestClient.SUCCESS.test(response.getStatusCode())) { + RestException restException = RestException.fromJson( + response.getStream(), + client.getObjectMapper() + ); + + if (restException == null) { + throw new ApiException( + "Server Error, no content", + response.getStatusCode() + ); + } + throw new ApiException(restException); + } + + Page firstPage = Page.fromJson( + "messages", + response.getContent(), + Message.class, + client.getObjectMapper() + ); + + ResourceSet resourceSet = new ResourceSet<>(this, client, firstPage); + + return new PageMetadata<>( + resourceSet, + response.getStatusCode(), + response.getHeadersMap() + ); + } + + private Page pageForRequest( final TwilioRestClient client, final Request request @@ -222,5 +282,8 @@ private void addQueryParams(final Request request) { ParameterType.QUERY ); } + if(getPageSize() != null) { + request.addQueryParam("PageSize", Integer.toString(getPageSize())); + } } }