aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorOla Aunronning <olaa@yahooinc.com>2023-10-24 10:08:12 +0200
committerOla Aunronning <olaa@yahooinc.com>2023-10-24 10:08:17 +0200
commit900209e698fc4921103c505ed357eb368059e1a4 (patch)
tree92f27eb279e183efa8a133cd263de94afb100ccf /controller-server
parent0b6956a16bea480d9acbc0a03c747f69d69aaba8 (diff)
Add tax code/purchase order/invoice email
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java15
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java27
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java167
4 files changed, 195 insertions, 20 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
index d5bb47c94b0..087e7d54e12 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializer.java
@@ -93,6 +93,9 @@ public class TenantSerializer {
private static final String cloudAccountsField = "cloudAccounts";
private static final String accountField = "account";
private static final String templateVersionField = "templateVersion";
+ private static final String taxCodeField = "taxCode";
+ private static final String purchaseOrderField = "purchaseOrder";
+ private static final String invoiceEmailField = "invoiceEmail";
private static final String awsIdField = "awsId";
private static final String roleField = "role";
@@ -282,12 +285,19 @@ public class TenantSerializer {
}
private TenantBilling tenantInfoBillingContactFromSlime(Inspector billingObject) {
+ var taxCode = billingObject.field(taxCodeField).asString();
+ var purchaseOrder = billingObject.field(purchaseOrderField).asString();
+ var invoiceEmail = billingObject.field(invoiceEmailField).asString();
+
return TenantBilling.empty()
.withContact(TenantContact.from(
billingObject.field("name").asString(),
new Email(billingObject.field("email").asString(), billingObject.field("emailVerified").asBool()),
billingObject.field("phone").asString()))
- .withAddress(tenantInfoAddressFromSlime(billingObject.field("address")));
+ .withAddress(tenantInfoAddressFromSlime(billingObject.field("address")))
+ .withTaxCode(taxCode)
+ .withPurchaseOrder(purchaseOrder)
+ .withInvoiceEmail(invoiceEmail);
}
private List<TenantSecretStore> secretStoresFromSlime(Inspector secretStoresObject) {
@@ -349,6 +359,9 @@ public class TenantSerializer {
billingCursor.setString("email", billingContact.contact().email().getEmailAddress());
billingCursor.setBool("emailVerified", billingContact.contact().email().isVerified());
billingCursor.setString("phone", billingContact.contact().phone());
+ billingCursor.setString(taxCodeField, billingContact.getTaxCode());
+ billingCursor.setString(purchaseOrderField, billingContact.getPurchaseOrder());
+ billingCursor.setString(invoiceEmailField, billingContact.getInvoiceEmail());
toSlime(billingContact.address(), billingCursor);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 6a6c8a51d72..d1d65f85dd3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -694,6 +694,9 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
contact.setString("email", billingContact.contact().email().getEmailAddress());
contact.setBool("emailVerified", billingContact.contact().email().isVerified());
contact.setString("phone", billingContact.contact().phone());
+ root.setString("taxCode", billingContact.getTaxCode());
+ root.setString("purchaseOrder", billingContact.getPurchaseOrder());
+ root.setString("invoiceEmail", billingContact.getInvoiceEmail());
toSlime(billingContact.address(), root); // will create "address" on the parent
}
@@ -708,10 +711,16 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
var mergedContact = updateBillingContact(inspector.field("contact"), cloudTenant.name(), contact);
var mergedAddress = updateTenantInfoAddress(inspector.field("address"), info.billingContact().address());
+ var mergedTaxCode = getString(inspector.field("taxCode"), info.billingContact().getTaxCode());
+ var mergedPurchaseOrder = getString(inspector.field("purchaseOrder"), info.billingContact().getPurchaseOrder());
+ var mergedInvoiceEmail = getString(inspector.field("invoiceEmail"), info.billingContact().getInvoiceEmail());
var mergedBilling = info.billingContact()
.withContact(mergedContact)
- .withAddress(mergedAddress);
+ .withAddress(mergedAddress)
+ .withTaxCode(mergedTaxCode)
+ .withPurchaseOrder(mergedPurchaseOrder)
+ .withInvoiceEmail(mergedInvoiceEmail);
var mergedInfo = info.withBilling(mergedBilling);
@@ -764,6 +773,11 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
throw new IllegalArgumentException("'website' needs to be a valid address");
}
}
+ if (! mergedInfo.billingContact().getInvoiceEmail().isBlank()) {
+ // TODO: Validate invoice email is set if collection method is INVOICE
+ if (! mergedInfo.billingContact().getInvoiceEmail().contains("@"))
+ throw new IllegalArgumentException("'Invoice email' needs to be an email address");
+ }
}
private void toSlime(TenantAddress address, Cursor parentCursor) {
@@ -785,6 +799,9 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
billingCursor.setString("email", billingContact.contact().email().getEmailAddress());
billingCursor.setBool("emailVerified", billingContact.contact().email().isVerified());
billingCursor.setString("phone", billingContact.contact().phone());
+ billingCursor.setString("taxCode", billingContact.getTaxCode());
+ billingCursor.setString("purchaseOrder", billingContact.getPurchaseOrder());
+ billingCursor.setString("invoiceEmail", billingContact.getInvoiceEmail());
toSlime(billingContact.address(), billingCursor);
}
@@ -914,9 +931,15 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
private TenantBilling updateTenantInfoBillingContact(Inspector insp, TenantName tenantName, TenantBilling oldContact) {
if (!insp.valid()) return oldContact;
+ var taxCode = getString(insp.field("taxCode"), oldContact.getTaxCode());
+ var purchaseOrder = getString(insp.field("purchaseOrder"), oldContact.getPurchaseOrder());
+ var invoiceEmail = getString(insp.field("purchaseOrder"), oldContact.getInvoiceEmail());
return TenantBilling.empty()
.withContact(updateBillingContact(insp, tenantName, oldContact.contact()))
- .withAddress(updateTenantInfoAddress(insp.field("address"), oldContact.address()));
+ .withAddress(updateTenantInfoAddress(insp.field("address"), oldContact.address()))
+ .withTaxCode(taxCode)
+ .withPurchaseOrder(purchaseOrder)
+ .withInvoiceEmail(invoiceEmail);
}
private TenantContacts updateTenantInfoContacts(Inspector insp, TenantName tenantName, TenantContacts oldContacts) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
index 9de856971c9..31794aad350 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/TenantSerializerTest.java
@@ -234,7 +234,11 @@ public class TenantSerializerTest {
.withCity("Suddery")
.withCountry("Sodor")
.withAddress("Central Station")
- .withRegion("Irish Sea")));
+ .withRegion("Irish Sea"))
+ .withPurchaseOrder("PO42")
+ .withTaxCode("1234L")
+ .withInvoiceEmail("billing@mycomp.any")
+ );
Slime slime = new Slime();
Cursor parentCursor = slime.setObject();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 2b01d87c903..1b6425a324a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -98,19 +98,68 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
@Test
void tenant_info_billing() {
+ var expectedResponse = """
+ {
+ "contact": {
+ "name":"",
+ "email":"",
+ "emailVerified":false,
+ "phone":""
+ },
+ "taxCode":"",
+ "purchaseOrder":"",
+ "invoiceEmail":""
+ }
+ """;
var request = request("/application/v4/tenant/scoober/info/billing", GET)
.roles(Set.of(Role.reader(tenantName)));
- tester.assertResponse(request, "{\"contact\":{\"name\":\"\",\"email\":\"\",\"emailVerified\":false,\"phone\":\"\"}}", 200);
-
- var fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
- var fullBillingContact = "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"phone\":\"phone\"},\"address\":" + fullAddress + "}";
-
+ tester.assertJsonResponse(request, expectedResponse, 200);
+
+ var fullBillingContact = """
+ {
+ "contact": {
+ "name":"name",
+ "email":"foo@example",
+ "phone":"phone"
+ },
+ "taxCode":"1234L",
+ "purchaseOrder":"PO9001",
+ "invoiceEmail":"billing@mycomp.any",
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ }
+ }
+ """;
var updateRequest = request("/application/v4/tenant/scoober/info/billing", PUT)
.data(fullBillingContact)
.roles(Set.of(Role.administrator(tenantName)));
tester.assertResponse(updateRequest, "{\"message\":\"Tenant info updated\"}", 200);
- tester.assertResponse(request, "{\"contact\":{\"name\":\"name\",\"email\":\"foo@example\",\"emailVerified\":false,\"phone\":\"phone\"},\"address\":{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}}", 200);
+ expectedResponse = """
+ {
+ "contact": {
+ "name":"name",
+ "email":"foo@example",
+ "emailVerified": false,
+ "phone":"phone"
+ },
+ "taxCode":"1234L",
+ "purchaseOrder":"PO9001",
+ "invoiceEmail":"billing@mycomp.any",
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ }
+ }
+ """;
+ tester.assertJsonResponse(request, expectedResponse, 200);
}
@Test
@@ -120,12 +169,32 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(request, "{\"contacts\":[{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"developer@scoober\",\"emailVerified\":true}]}", 200);
- var fullContacts = "{\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false},{\"audiences\":[\"notifications\"],\"email\":\"contact2@example.com\",\"emailVerified\":false},{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"contact3@example.com\",\"emailVerified\":false}]}";
+ var fullContacts = """
+ {
+ "contacts":[
+ {
+ "audiences":["tenant"]
+ ,"email":"contact1@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["notifications"],
+ "email":"contact2@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["tenant","notifications"],
+ "email":"contact3@example.com",
+ "emailVerified":false
+ }
+ ]
+ }
+ """;
var updateRequest = request("/application/v4/tenant/scoober/info/contacts", PUT)
.data(fullContacts)
.roles(Set.of(Role.administrator(tenantName)));
tester.assertResponse(updateRequest, "{\"message\":\"Tenant info updated\"}", 200);
- tester.assertResponse(request, fullContacts, 200);
+ tester.assertJsonResponse(request, fullContacts, 200);
}
@Test
@@ -150,13 +219,79 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(postPartialContacts, "{\"message\":\"Tenant info updated\"}", 200);
// Read back the updated info
- tester.assertResponse(infoRequest, "{\"name\":\"\",\"email\":\"\",\"website\":\"\",\"contactName\":\"newName\",\"contactEmail\":\"foo@example.com\",\"contactEmailVerified\":false,\"billingContact\":{\"name\":\"billingName\",\"email\":\"\",\"emailVerified\":false,\"phone\":\"\"},\"contacts\":[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false}]}", 200);
-
- String fullAddress = "{\"addressLines\":\"addressLines\",\"postalCodeOrZip\":\"postalCodeOrZip\",\"city\":\"city\",\"stateRegionProvince\":\"stateRegionProvince\",\"country\":\"country\"}";
- String fullBillingContact = "{\"name\":\"name\",\"email\":\"foo@example\",\"emailVerified\":false,\"phone\":\"phone\",\"address\":" + fullAddress + "}";
- String fullContacts = "[{\"audiences\":[\"tenant\"],\"email\":\"contact1@example.com\",\"emailVerified\":false},{\"audiences\":[\"notifications\"],\"email\":\"contact2@example.com\",\"emailVerified\":false},{\"audiences\":[\"tenant\",\"notifications\"],\"email\":\"contact3@example.com\",\"emailVerified\":false}]";
- String fullInfo = "{\"name\":\"name\",\"email\":\"foo@example\",\"website\":\"https://yahoo.com\",\"contactName\":\"contactName\",\"contactEmail\":\"contact@example.com\",\"contactEmailVerified\":false,\"address\":" + fullAddress + ",\"billingContact\":" + fullBillingContact + ",\"contacts\":" + fullContacts + "}";
-
+ var expectedResponse = """
+ {
+ "name":"",
+ "email":"",
+ "website":"",
+ "contactName":"newName",
+ "contactEmail":"foo@example.com",
+ "contactEmailVerified":false,
+ "billingContact": {
+ "name":"billingName",
+ "email":"","emailVerified":false,
+ "phone":"",
+ "taxCode":"",
+ "purchaseOrder":"",
+ "invoiceEmail":""
+ },
+ "contacts": [
+ {"audiences":["tenant"],"email":"contact1@example.com","emailVerified":false}
+ ]
+ }
+ """;
+ tester.assertJsonResponse(infoRequest, expectedResponse, 200);
+
+ var fullInfo = """
+ {
+ "name":"name",
+ "email":"foo@example",
+ "website":"https://yahoo.com",
+ "contactName":"contactName",
+ "contactEmail":"contact@example.com",
+ "contactEmailVerified":false,
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ },
+ "billingContact": {
+ "name":"name",
+ "email":"foo@example",
+ "emailVerified":false,
+ "phone":"phone",
+ "taxCode":"",
+ "purchaseOrder":"",
+ "invoiceEmail":"",
+ "address": {
+ "addressLines":"addressLines",
+ "postalCodeOrZip":"postalCodeOrZip",
+ "city":"city",
+ "stateRegionProvince":"stateRegionProvince",
+ "country":"country"
+ }
+ },
+ "contacts": [
+ {
+ "audiences":["tenant"],
+ "email":"contact1@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["notifications"],
+ "email":"contact2@example.com",
+ "emailVerified":false
+ },
+ {
+ "audiences":["tenant","notifications"]
+ ,"email":"contact3@example.com",
+ "emailVerified":false
+ }
+ ]
+ }
+ """;
// Now set all fields
var postFull =
request("/application/v4/tenant/scoober/info", PUT)
@@ -165,7 +300,7 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
tester.assertResponse(postFull, "{\"message\":\"Tenant info updated\"}", 200);
// Now compare the updated info with the full info we sent
- tester.assertResponse(infoRequest, fullInfo, 200);
+ tester.assertJsonResponse(infoRequest, fullInfo, 200);
var invalidBody = "{\"mail\":\"contact1@example.com\", \"mailType\":\"blurb\"}";
var resendMailRequest =