Skip to content

Commit d181b60

Browse files
author
Pedro González Marcos
committed
feat: add Contracts endpoints domain models
1 parent 2d6e9f2 commit d181b60

4 files changed

Lines changed: 266 additions & 73 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package io.github.pgmarc.space.contracts;
2+
3+
import java.time.Duration;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.HashSet;
7+
import java.util.Objects;
8+
import java.util.Set;
9+
10+
public final class SubscriptionRequest {
11+
12+
private final UserContact userContact;
13+
private final Set<Service> services;
14+
private final Duration renewalDays;
15+
16+
private SubscriptionRequest(Builder builder) {
17+
this.userContact = builder.userContact;
18+
this.renewalDays = builder.renewalDays;
19+
this.services = Collections.unmodifiableSet(builder.services);
20+
}
21+
22+
public static Builder builder(UserContact userContact) {
23+
return new Builder(userContact);
24+
}
25+
26+
public UserContact getUserContact() {
27+
return userContact;
28+
}
29+
30+
public Set<Service> getServices() {
31+
return services;
32+
}
33+
34+
public Duration getRenewalDays() {
35+
return renewalDays;
36+
}
37+
38+
public static final class Builder {
39+
40+
private final UserContact userContact;
41+
private final Set<Service> services = new HashSet<>();
42+
private Service.Builder serviceBuilder;
43+
private Duration renewalDays;
44+
45+
private Builder(UserContact userContact) {
46+
this.userContact = userContact;
47+
}
48+
49+
private boolean isServiceBuilderAlive() {
50+
return serviceBuilder != null;
51+
}
52+
53+
private void validateServiceBuilderCalled(String message) {
54+
if (!isServiceBuilderAlive()) {
55+
throw new IllegalStateException(message);
56+
}
57+
}
58+
59+
public Builder service(String name, String version) {
60+
if (isServiceBuilderAlive()) {
61+
throw new IllegalStateException("you must build a service before creating another");
62+
}
63+
this.serviceBuilder = Service.builder(name, version);
64+
return this;
65+
}
66+
67+
public Builder plan(String plan) {
68+
validateServiceBuilderCalled("you must call 'newService' before setting a plan: " + plan);
69+
serviceBuilder.plan(plan);
70+
return this;
71+
}
72+
73+
public Builder addOn(String addOnName, long quantity) {
74+
validateServiceBuilderCalled("you must call 'newService' before setting an add-on: " + addOnName);
75+
serviceBuilder.addOn(addOnName, quantity);
76+
return this;
77+
}
78+
79+
private void destroyServiceBuilder() {
80+
this.serviceBuilder = null;
81+
}
82+
83+
public Builder buildService() {
84+
validateServiceBuilderCalled("you must call 'newService' before adding a service");
85+
services.add(serviceBuilder.build());
86+
destroyServiceBuilder();
87+
return this;
88+
}
89+
90+
public Builder subscribe(Service service) {
91+
this.services.add(Objects.requireNonNull(service, "service must not be null"));
92+
return this;
93+
}
94+
95+
public Builder subscribeAll(Collection<Service> services) {
96+
Objects.requireNonNull(services, "services must not be null");
97+
this.services.addAll(services);
98+
return this;
99+
}
100+
101+
public Builder renewIn(Duration renewalDays) {
102+
this.renewalDays = renewalDays;
103+
return this;
104+
}
105+
106+
public SubscriptionRequest build() {
107+
Objects.requireNonNull(userContact, "userContact must not be null");
108+
return new SubscriptionRequest(this);
109+
}
110+
}
111+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.github.pgmarc.space.contracts;
2+
3+
import java.util.HashSet;
4+
import java.util.Objects;
5+
import java.util.Set;
6+
7+
public class SubscriptionUpdateRequest {
8+
9+
private final Set<Service> services = new HashSet<>();
10+
private Service.Builder serviceBuilder;
11+
12+
private SubscriptionUpdateRequest() {
13+
14+
}
15+
16+
public Set<Service> getServices() {
17+
return services;
18+
}
19+
20+
public static SubscriptionUpdateRequest builder() {
21+
return new SubscriptionUpdateRequest();
22+
}
23+
24+
public SubscriptionUpdateRequest service(String name, String version) {
25+
this.serviceBuilder = Service.builder(name, version);
26+
return this;
27+
}
28+
29+
public SubscriptionUpdateRequest plan(String plan) {
30+
Objects.requireNonNull(serviceBuilder, "you call service first");
31+
serviceBuilder.plan(plan);
32+
return this;
33+
}
34+
35+
public SubscriptionUpdateRequest addOn(String name, long quantity) {
36+
Objects.requireNonNull(serviceBuilder, "you call service first");
37+
this.serviceBuilder.addOn(name, 0);
38+
return this;
39+
}
40+
41+
public SubscriptionUpdateRequest add() {
42+
Objects.requireNonNull(serviceBuilder, "you call service first");
43+
this.services.add(serviceBuilder.build());
44+
this.serviceBuilder = null;
45+
return this;
46+
}
47+
48+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package io.github.pgmarc.space.contracts;
2+
3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
7+
import java.time.Duration;
8+
import java.time.ZonedDateTime;
9+
import java.util.Set;
10+
11+
import org.junit.jupiter.api.BeforeEach;
12+
import org.junit.jupiter.api.Test;
13+
14+
class SubscriptionRequestTest {
15+
16+
private static final UserContact TEST_USER_CONTACT = UserContact.builder("123456789", "alexdoe")
17+
.build();
18+
19+
private BillingPeriod billingPeriod;
20+
21+
@BeforeEach
22+
void setUp() {
23+
ZonedDateTime start = ZonedDateTime.parse("2025-08-15T00:00:00Z");
24+
ZonedDateTime end = start.plusDays(30);
25+
billingPeriod = BillingPeriod.of(start, end);
26+
billingPeriod.setRenewalDays(Duration.ofDays(30));
27+
}
28+
29+
@Test
30+
void givenMultipleServicesInSubscriptionShouldCreate() {
31+
32+
long renewalDays = 30;
33+
String service1Name = "Petclinic";
34+
String service2Name = "Petclinic Labs";
35+
36+
Service service1 = Service.builder(service1Name, "v1").plan("GOLD").build();
37+
Service service2 = Service.builder(service2Name, "v2").plan("PLATINUM").build();
38+
39+
SubscriptionRequest sub = SubscriptionRequest
40+
.builder(TEST_USER_CONTACT)
41+
.subscribe(service1)
42+
.subscribe(service2)
43+
.renewIn(Duration.ofDays(renewalDays))
44+
.build();
45+
46+
assertEquals(Set.of(service1, service2), sub.getServices());
47+
}
48+
49+
@Test
50+
void givenConsecutiveServiceCreationShouldThrow() {
51+
52+
Exception ex = assertThrows(IllegalStateException.class, () -> SubscriptionRequest
53+
.builder(TEST_USER_CONTACT)
54+
.service("test", "v1")
55+
.service("incorrect", "v1")
56+
.build());
57+
assertEquals("you must build a service before creating another", ex.getMessage());
58+
}
59+
60+
@Test
61+
void givenPlanCallBeforeCallingCreationServiceShouldThrow() {
62+
63+
Exception ex = assertThrows(IllegalStateException.class, () -> SubscriptionRequest
64+
.builder(TEST_USER_CONTACT)
65+
.plan("foo")
66+
.build());
67+
assertEquals("you must call 'newService' before setting a plan: foo", ex.getMessage());
68+
}
69+
70+
@Test
71+
void givenAddOnCallBeforeCallingCreationServiceShouldThrow() {
72+
73+
Exception ex = assertThrows(IllegalStateException.class, () -> SubscriptionRequest
74+
.builder(TEST_USER_CONTACT)
75+
.addOn("foo", 1)
76+
.build());
77+
assertEquals("you must call 'newService' before setting an add-on: foo", ex.getMessage());
78+
}
79+
80+
@Test
81+
void givenServiceBuildCallBeforeCreationServiceShouldThrow() {
82+
83+
Exception ex = assertThrows(IllegalStateException.class, () -> SubscriptionRequest
84+
.builder(TEST_USER_CONTACT)
85+
.buildService()
86+
.build());
87+
assertEquals("you must call 'newService' before adding a service", ex.getMessage());
88+
}
89+
90+
@Test
91+
void whenNoRequiredParametersInputShouldThrow() {
92+
93+
Exception ex = assertThrows(NullPointerException.class,
94+
() -> SubscriptionRequest.builder(null)
95+
.build());
96+
assertEquals("userContact must not be null", ex.getMessage());
97+
}
98+
99+
@Test
100+
void givenOptionalRenewalDaysShouldNotThrow() {
101+
102+
assertDoesNotThrow(() -> SubscriptionRequest.builder(TEST_USER_CONTACT)
103+
.renewIn(null)
104+
.build());
105+
}
106+
107+
}

src/test/java/io/github/pgmarc/space/contracts/SubscriptionTest.java

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)