diff --git a/.github/workflows/test-and-deploy.yml b/.github/workflows/test-and-deploy.yml index 07178db5f3..d9aace8a70 100644 --- a/.github/workflows/test-and-deploy.yml +++ b/.github/workflows/test-and-deploy.yml @@ -62,7 +62,7 @@ jobs: with: distribution: 'zulu' java-version: '17' - + - name: SonarCloud Scan if: ${{ (github.event_name == 'pull_request' || github.ref_type == 'branch') && matrix.java == 11 && !github.event.pull_request.head.repo.fork }} env: @@ -94,7 +94,7 @@ jobs: - name: Create GitHub Release uses: sendgrid/dx-automator/actions/release@main with: - footer: '**[Maven](https://mvnrepository.com/artifact/com.twilio.sdk/twilio/${version})**' + footer: '**[Maven](https://central.sonatype.com/artifact/com.twilio.sdk/twilio/${version})**' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/main/java/com/twilio/base/ResourceSet.java b/src/main/java/com/twilio/base/ResourceSet.java index 37fec6c969..6e5608adce 100644 --- a/src/main/java/com/twilio/base/ResourceSet.java +++ b/src/main/java/com/twilio/base/ResourceSet.java @@ -14,6 +14,7 @@ public class ResourceSet implements Iterable { private final Reader reader; private final TwilioRestClient client; + private final Page firstPage; // Store reference to first page to enable multiple iterations private boolean autoPaging; private long pages = 1; @@ -32,6 +33,7 @@ public class ResourceSet implements Iterable { public ResourceSet(final Reader reader, final TwilioRestClient client, final Page page) { this.reader = 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; @@ -74,6 +76,12 @@ public long getPageLimit() { @Override public Iterator iterator() { + // Reset state to allow multiple iterations + this.processed = 0; + this.pages = 1; + this.page = this.firstPage; // Reset to first page for new iteration + this.iterator = this.firstPage.getRecords().iterator(); // Reset iterator to start of first page + return new ResourceSetIterator<>(this); } diff --git a/src/main/java/com/twilio/base/bearertoken/ResourceSet.java b/src/main/java/com/twilio/base/bearertoken/ResourceSet.java index 16bc369084..4cc09d8c2d 100644 --- a/src/main/java/com/twilio/base/bearertoken/ResourceSet.java +++ b/src/main/java/com/twilio/base/bearertoken/ResourceSet.java @@ -14,6 +14,7 @@ public class ResourceSet implements Iterable { private final Reader reader; private final BearerTokenTwilioRestClient client; + private final Page firstPage; // Store reference to first page to enable multiple iterations private boolean autoPaging; private long pages = 1; @@ -32,6 +33,7 @@ public class ResourceSet implements Iterable { public ResourceSet(final Reader reader, final BearerTokenTwilioRestClient client, final Page page) { this.reader = 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; @@ -74,6 +76,12 @@ public long getPageLimit() { @Override public Iterator iterator() { + // Reset state to allow multiple iterations + this.processed = 0; + this.pages = 1; + this.page = this.firstPage; // Reset to first page for new iteration + this.iterator = this.firstPage.getRecords().iterator(); // Reset iterator to start of first page + return new ResourceSetIterator<>(this); } diff --git a/src/main/java/com/twilio/type/App.java b/src/main/java/com/twilio/type/App.java new file mode 100644 index 0000000000..0ca077d6b5 --- /dev/null +++ b/src/main/java/com/twilio/type/App.java @@ -0,0 +1,46 @@ +package com.twilio.type; + +import java.util.Objects; + +public class App implements Endpoint { + + public static final String PREFIX = "app:"; + + private final String app; + + public App(String app) { + if (!app.toLowerCase().startsWith(PREFIX)) { + app = PREFIX + app; + } + + this.app = app; + } + + @Override + public String getEndpoint() { + return this.app; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + App other = (App) o; + return Objects.equals(this.app, other.app); + } + + @Override + public int hashCode() { + return Objects.hash(this.app); + } + + @Override + public String toString() { + return this.app; + } +} diff --git a/src/test/java/com/twilio/base/ResourceSetIterationTest.java b/src/test/java/com/twilio/base/ResourceSetIterationTest.java new file mode 100644 index 0000000000..7cc7a8b80f --- /dev/null +++ b/src/test/java/com/twilio/base/ResourceSetIterationTest.java @@ -0,0 +1,148 @@ +package com.twilio.base; + +import com.twilio.http.TwilioRestClient; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import static org.mockito.Mockito.when; + +// Simple test resource for testing ResourceSet iteration +class TestResource extends Resource { + private final String id; + + public TestResource(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} + +// Simple test reader for testing ResourceSet iteration +class TestReader extends Reader { + @Override + public ResourceSet read(TwilioRestClient client) { + return null; // Not needed for our test + } + + @Override + public Page firstPage(TwilioRestClient client) { + return null; // Not needed for our test + } + + @Override + public Page previousPage(Page page, TwilioRestClient client) { + return null; // Not needed for our test + } + + @Override + public Page nextPage(Page page, TwilioRestClient client) { + return null; // Not needed for our test + } + + @Override + public Page getPage(String targetUrl, TwilioRestClient client) { + return null; // Not needed for our test + } +} + +public class ResourceSetIterationTest { + + @Mock + TwilioRestClient client; + + @Mock + Page page; + + @Before + public void init() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testResourceSetCanBeIteratedMultipleTimes() { + // Setup mock data with concrete objects + TestResource resource1 = new TestResource("resource1"); + TestResource resource2 = new TestResource("resource2"); + List resources = Arrays.asList(resource1, resource2); + + when(page.getRecords()).thenReturn(resources); + when(page.getPageSize()).thenReturn(2); + when(page.hasNextPage()).thenReturn(false); + + Reader reader = new TestReader().limit(2); + ResourceSet resourceSet = new ResourceSet<>(reader, client, page); + + // First iteration - should work + List firstIterationResults = new ArrayList<>(); + for (TestResource resource : resourceSet) { + firstIterationResults.add(resource.getId()); + } + Assert.assertEquals(2, firstIterationResults.size()); + Assert.assertEquals("resource1", firstIterationResults.get(0)); + Assert.assertEquals("resource2", firstIterationResults.get(1)); + + // Second iteration - should also work and return the same results + List secondIterationResults = new ArrayList<>(); + for (TestResource resource : resourceSet) { + secondIterationResults.add(resource.getId()); + } + Assert.assertEquals("Second iteration should return same number of elements", 2, secondIterationResults.size()); + Assert.assertEquals("resource1", secondIterationResults.get(0)); + Assert.assertEquals("resource2", secondIterationResults.get(1)); + + // Third iteration using explicit iterator - should also work + List thirdIterationResults = new ArrayList<>(); + for (Iterator it = resourceSet.iterator(); it.hasNext(); ) { + TestResource resource = it.next(); + thirdIterationResults.add(resource.getId()); + } + Assert.assertEquals("Third iteration should return same number of elements", 2, thirdIterationResults.size()); + Assert.assertEquals("resource1", thirdIterationResults.get(0)); + Assert.assertEquals("resource2", thirdIterationResults.get(1)); + } + + @Test + public void testResourceSetWithLimitCanBeIteratedMultipleTimes() { + // Setup mock data with more resources than the limit + TestResource resource1 = new TestResource("resource1"); + TestResource resource2 = new TestResource("resource2"); + TestResource resource3 = new TestResource("resource3"); + List resources = Arrays.asList(resource1, resource2, resource3); + + when(page.getRecords()).thenReturn(resources); + when(page.getPageSize()).thenReturn(3); + when(page.hasNextPage()).thenReturn(false); + + // Set limit to 2, so only first 2 should be returned + Reader reader = new TestReader().limit(2); + ResourceSet resourceSet = new ResourceSet<>(reader, client, page); + + // First iteration - should only return 2 elements due to limit + List firstIterationResults = new ArrayList<>(); + for (TestResource resource : resourceSet) { + firstIterationResults.add(resource.getId()); + } + Assert.assertEquals("First iteration should respect limit", 2, firstIterationResults.size()); + Assert.assertEquals("resource1", firstIterationResults.get(0)); + Assert.assertEquals("resource2", firstIterationResults.get(1)); + + // Second iteration - should also respect limit and return same results + List secondIterationResults = new ArrayList<>(); + for (TestResource resource : resourceSet) { + secondIterationResults.add(resource.getId()); + } + Assert.assertEquals("Second iteration should respect limit", 2, secondIterationResults.size()); + Assert.assertEquals("resource1", secondIterationResults.get(0)); + Assert.assertEquals("resource2", secondIterationResults.get(1)); + } +} \ No newline at end of file diff --git a/src/test/java/com/twilio/type/AppTest.java b/src/test/java/com/twilio/type/AppTest.java new file mode 100644 index 0000000000..e1f1760e51 --- /dev/null +++ b/src/test/java/com/twilio/type/AppTest.java @@ -0,0 +1,17 @@ +package com.twilio.type; + +import org.junit.Assert; +import org.junit.Test; + +public class AppTest { + + @Test + public void testGetEndpoint() { + Assert.assertEquals("app:me", new App("me").getEndpoint()); + Assert.assertEquals("app:YOU", new App("YOU").getEndpoint()); + Assert.assertEquals("APP:HIM", new App("APP:HIM").getEndpoint()); + Assert.assertEquals("aPp:her", new App("aPp:her").getEndpoint()); + Assert.assertEquals("app:AP12345?mycustomparam1=foo&mycustomparam2=bar", new App("app:AP12345?mycustomparam1=foo&mycustomparam2=bar").getEndpoint()); + Assert.assertEquals("app:AP12345?mycustomparam1=foo&mycustomparam2=bar", new App("AP12345?mycustomparam1=foo&mycustomparam2=bar").getEndpoint()); + } +} \ No newline at end of file