diff --git a/API-implementation-tracking.md b/API-implementation-tracking.md new file mode 100644 index 0000000..6abc414 --- /dev/null +++ b/API-implementation-tracking.md @@ -0,0 +1,118 @@ + +## Customer Operations +~~Assign licenses to a user~~
+~~Get licenses assigned to a user~~
+Get licenses assigned to a user by license group
+~~Create a customer~~
+Create a customer for an indirect reseller
+Create user accounts for a customer
+Delete a user account for a customer
+Get a list of customers filtered by a search field
+Get a customer by ID
+~~Get a customer's billing profile~~
+Get a customer's company profile
+Get a customer's orders
+Get a customer's subscriptions
+Get a list of all user accounts for a customer
+Get a list of available licenses
+Get a list of available licenses by license group
+Get a list of customers
+Get a user account by ID
+Get user roles for a customer
+Remove a customer user from a role
+Retrieve a relationship request URL
+Reset user password for a customer
+Restore a deleted user for a customer
+Set user roles for a customer
+Update a customer's billing profile
+Update the nickname for a subscription
+Update user accounts for a customer
+View deleted users for a customer
+ +## Order Operations +Change the quantity of a subscription
+[Convert a trial subscription to paid](convert-a-trial-subscription-to-paid.md
+Create an order
+Create an order for a customer of an indirect reseller
+Get a list of add-ons for a subscription
+Get a list of offer categories by market
+Get a list of offers for a market
+Get a list of subscriptions by order
+Get a list of trial conversion offers
+Get a subscription by ID
+Get add-ons for an offer ID
+Get an offer by ID
+Get an order by ID
+Get subscription provisioning status
+Purchase an add-on to a subscription
+Reactivate a suspended subscription
+Remove a reseller relationship with a customer
+Suspend a subscription
+Transition a subscription
+ +## Audit Operations +Get a record of Partner Center activity + +## Analytics Operations +Get partner licenses deployment information
+Get partner licenses usage information
+Get customer licenses deployment information
+Get customer licenses usage information
+ +## Billing Operations +Get a customer's service costs summary
+Get a customer's service costs line items
+Get a collection of invoices
+Get a customer's utilization records for Azure
+Get an invoice by ID
+Get invoice line items
+Get prices for Microsoft Azure
+Get prices for Microsoft Azure Partner Shared Services
+Get the partner's current account balance
+Get usage data for a subscription
+Get a usage summary for all of a customer's subscriptions
+ +## Support Operations +Create a service request
+Get a subscription's support contact
+Get all service requests for a customer
+Get service request support topics
+Get the managed services for a customer by ID
+Update a service request
+Update a subscription's support contact
+ +## Accounts and Profile Operations +Get a customer's subscriptions by partner MPN ID
+Get an organization profile
+Get customers of an indirect reseller
+Get indirect resellers of a customer
+Get Microsoft Partner Network profile
+Get partner billing profile
+Verify a partner MPN ID
+Get support profile
+Get the partner legal business profile
+Retrieve a list of indirect resellers
+Update an organization profile
+Update the partner billing profile
+Update support profile
+Update the partner legal business profile
+ +## Utility Operations +Validate an address
+Get address formatting rules by market
+Verify domain availability
+Delete a customer account from the integration sandbox
+ +## Device Deployment Operations +Create a new configuration policy for the specified customer
+Delete a configuration policy for the specified customer
+Get a list of a customer's policies
+Retrieve a customer's configuration policy
+Update a configuration policy for the specified customer
+Get the status of a device batch upload
+Get a list of device batches for the specified customer
+Get a list of devices for the specified batch and customer
+Upload a list of devices to create a new batch for the specified customer
+Upload a list of devices to an existing batch for the specified customer
+Update a list of devices with a policy
+Delete a device for the specified customer
diff --git a/release-notes.md b/release-notes.md index cbd5025..1760a83 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,5 +1,9 @@ # Release Notes: Spring Social for Microsoft Partner Center +## 6.2.0 +#### Added +1. ServiceRequestOperations added to allow for creating and retrieving service requests through the API + ## 6.1.0 #### Bug Fixes diff --git a/src/main/java/org/springframework/social/partnercenter/api/customer/AdminCustomerOperations.java b/src/main/java/org/springframework/social/partnercenter/api/customer/AdminCustomerOperations.java index 21621e1..0e6d7ee 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/customer/AdminCustomerOperations.java +++ b/src/main/java/org/springframework/social/partnercenter/api/customer/AdminCustomerOperations.java @@ -7,5 +7,5 @@ public interface AdminCustomerOperations extends CustomerOperations{ ResponseEntity getBillingProfile(String customerTenantId); ResponseEntity updateBillingProfile(String customerTenantId, String etag, CustomerBillingProfile billingProfile); ResponseEntity> getCompanyByDomain(int size, String filter); - ResponseEntity> subscribedSkus(String customerTenantId); + ResponseEntity> subscribedSkus(String customerId); } diff --git a/src/main/java/org/springframework/social/partnercenter/api/customer/user/AdminUserOperations.java b/src/main/java/org/springframework/social/partnercenter/api/customer/user/AdminUserOperations.java index 3241564..a066f15 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/customer/user/AdminUserOperations.java +++ b/src/main/java/org/springframework/social/partnercenter/api/customer/user/AdminUserOperations.java @@ -19,4 +19,21 @@ public interface AdminUserOperations extends UserOperations{ ResponseEntity> getUserRoles(String customerTenantId, String userId); ResponseEntity> getAllRoles(String customerTenantId); ResponseEntity> getRolesByRoleId(String customerTenantId, String RoleId); + + /** + * Gets a list of deleted CustomerUser resources for a customer by customer ID. + * + * @param customerId The value is a GUID formatted customer-id that identifies the customer. + * @return List of deleted CustomerUsers + */ + ResponseEntity> getDeletedUsers(String customerId); + + /** + * Gets a list of deleted CustomerUser resources for a customer by customer ID. + * + * @param customerId The value is a GUID formatted customer-id that identifies the customer. + * @param size The maximum size of the list to be returned + * @return List of deleted CustomerUsers + */ + ResponseEntity> getDeletedUsers(String customerId, Integer size); } diff --git a/src/main/java/org/springframework/social/partnercenter/api/customer/user/impl/AdminUserTemplate.java b/src/main/java/org/springframework/social/partnercenter/api/customer/user/impl/AdminUserTemplate.java index 77cde9e..9d62523 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/customer/user/impl/AdminUserTemplate.java +++ b/src/main/java/org/springframework/social/partnercenter/api/customer/user/impl/AdminUserTemplate.java @@ -1,10 +1,14 @@ package org.springframework.social.partnercenter.api.customer.user.impl; +import static org.springframework.social.partnercenter.serialization.Json.toJson; + import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.ResponseEntity; import org.springframework.social.partnercenter.PartnerCenterAdmin; import org.springframework.social.partnercenter.api.PartnerCenterResponse; import org.springframework.social.partnercenter.api.customer.Role; +import org.springframework.social.partnercenter.api.customer.query.Filter; +import org.springframework.social.partnercenter.api.customer.query.Operator; import org.springframework.social.partnercenter.api.customer.user.AdminUserOperations; import org.springframework.social.partnercenter.api.customer.user.CustomerUser; import org.springframework.social.partnercenter.api.customer.user.License; @@ -108,6 +112,23 @@ public ResponseEntity> getRolesByRoleId(String custo .get(new ParameterizedTypeReference>() {}); } + @Override + public ResponseEntity> getDeletedUsers(String customerId) { + return restResource.request() + .pathSegment(customerId, "users") + .queryParam("filter", toJson(Filter.builder().field("UserState").operator(Operator.EQUALS).value("Inactive").build())) + .get(new ParameterizedTypeReference>() {}); + } + + @Override + public ResponseEntity> getDeletedUsers(String customerId, Integer size) { + return restResource.request() + .pathSegment(customerId, "users") + .queryParam("size", size) + .queryParam("filter", toJson(Filter.builder().field("UserState").operator(Operator.EQUALS).value("Inactive").build())) + .get(new ParameterizedTypeReference>() {}); + } + @Override protected String getProviderId() { return PartnerCenterAdmin.PROVIDER_ID; diff --git a/src/main/java/org/springframework/social/partnercenter/api/support/SupportOperations.java b/src/main/java/org/springframework/social/partnercenter/api/support/SupportOperations.java index 729d9ad..98cd9f6 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/support/SupportOperations.java +++ b/src/main/java/org/springframework/social/partnercenter/api/support/SupportOperations.java @@ -1,5 +1,7 @@ package org.springframework.social.partnercenter.api.support; +import java.util.Locale; + import org.springframework.http.ResponseEntity; import org.springframework.social.partnercenter.api.PartnerCenterResponse; import org.springframework.social.partnercenter.api.support.managedservice.ManagedService; @@ -12,5 +14,5 @@ public interface SupportOperations { ResponseEntity> getSupportTopics(); ResponseEntity> getServiceRequests(String customerId); ResponseEntity updateServiceRequest(ServiceRequest request); - ResponseEntity createServiceRequest(ServiceRequest request); + ResponseEntity createServiceRequest(ServiceRequest request, Locale locale); } diff --git a/src/main/java/org/springframework/social/partnercenter/api/support/impl/SupportTemplate.java b/src/main/java/org/springframework/social/partnercenter/api/support/impl/SupportTemplate.java index badc329..96d983a 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/support/impl/SupportTemplate.java +++ b/src/main/java/org/springframework/social/partnercenter/api/support/impl/SupportTemplate.java @@ -1,5 +1,7 @@ package org.springframework.social.partnercenter.api.support.impl; +import java.util.Locale; + import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.ResponseEntity; import org.springframework.social.partnercenter.PartnerCenter; @@ -69,9 +71,9 @@ public ResponseEntity updateServiceRequest(ServiceRequest reques } @Override - public ResponseEntity createServiceRequest(ServiceRequest request) { + public ResponseEntity createServiceRequest(ServiceRequest request, Locale locale) { return restResource.request() - .pathSegment(SERVICE_REQUESTS) + .pathSegment(SERVICE_REQUESTS, locale.toLanguageTag()) .post(request, ServiceRequest.class); } diff --git a/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequest.java b/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequest.java index a8a06d7..ffba6df 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequest.java +++ b/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequest.java @@ -13,6 +13,9 @@ public class ServiceRequest { private String id; private String title; + private String description; + private String supportTopicId; + private String supportTopicName; private ServiceRequestSeverity severity; private ServiceRequestStatus status; private ServiceRequestOrganization organization; @@ -29,6 +32,9 @@ public class ServiceRequest { private String countryCode; private ResourceAttributes attributes; + public ServiceRequest() { + } + public String getTitle() { return title; } @@ -164,4 +170,117 @@ public String getCountryCode() { public void setCountryCode(String countryCode) { this.countryCode = countryCode; } + + public static Builder builder() { + return new Builder(); + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getSupportTopicId() { + return supportTopicId; + } + + public void setSupportTopicId(String supportTopicId) { + this.supportTopicId = supportTopicId; + } + + public String getSupportTopicName() { + return supportTopicName; + } + + public void setSupportTopicName(String supportTopicName) { + this.supportTopicName = supportTopicName; + } + + public static class Builder { + private String title; + private String description; + private ServiceRequestSeverity severity; + private ServiceRequestOrganization organization; + private ServiceRequestContact primaryContact; + private String supportTopicId; + private String supportTopicName; + private String productName; + private String productId; + private ServiceRequestNote newNote; + private String countryCode; + + public Builder title(String title) { + this.title = title; + return this; + } + + public Builder severity(ServiceRequestSeverity severity) { + this.severity = severity; + return this; + } + + public Builder organization(ServiceRequestOrganization organization) { + this.organization = organization; + return this; + } + + public Builder primaryContact(ServiceRequestContact primaryContact) { + this.primaryContact = primaryContact; + return this; + } + + public Builder productName(String productName) { + this.productName = productName; + return this; + } + + public Builder productId(String productId) { + this.productId = productId; + return this; + } + + public Builder newNote(ServiceRequestNote newNote) { + this.newNote = newNote; + return this; + } + + public Builder countryCode(String countryCode) { + this.countryCode = countryCode; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public ServiceRequest build() { + ServiceRequest request = new ServiceRequest(); + request.setTitle(title); + request.setCountryCode(countryCode); + request.setSeverity(severity); + request.setOrganization(organization); + request.setPrimaryContact(primaryContact); + request.setProductName(productName); + request.setProductId(productId); + request.setNewNote(newNote); + request.setSupportTopicId(supportTopicId); + request.setSupportTopicName(supportTopicName); + request.setDescription(description); + return request; + } + + public Builder supportTopicId(String supportTopicId) { + this.supportTopicId = supportTopicId; + return this; + } + + public Builder supportTopicName(String supportTopicName) { + this.supportTopicName = supportTopicName; + return this; + } + } } diff --git a/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequestNote.java b/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequestNote.java index f622d17..c7888db 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequestNote.java +++ b/src/main/java/org/springframework/social/partnercenter/api/support/servicerequest/ServiceRequestNote.java @@ -35,4 +35,30 @@ public String getText() { public void setText(String text) { this.text = text; } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String createdByName; + private String text; + + public Builder createdByName(String createdByName) { + this.createdByName = createdByName; + return this; + } + + public Builder text(String text) { + this.text = text; + return this; + } + + public ServiceRequestNote build() { + ServiceRequestNote note = new ServiceRequestNote(); + note.setCreatedByName(createdByName); + note.setText(text); + return note; + } + } } diff --git a/src/main/java/org/springframework/social/partnercenter/api/utilities/impl/UtilityTemplate.java b/src/main/java/org/springframework/social/partnercenter/api/utilities/impl/UtilityTemplate.java index 1281e7c..b1cba22 100644 --- a/src/main/java/org/springframework/social/partnercenter/api/utilities/impl/UtilityTemplate.java +++ b/src/main/java/org/springframework/social/partnercenter/api/utilities/impl/UtilityTemplate.java @@ -3,12 +3,15 @@ import static java.time.ZoneId.of; import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.ResponseEntity.ok; import static org.springframework.social.partnercenter.serialization.Json.toJson; import static org.springframework.social.partnercenter.time.PartnerCenterDateTimeFormatter.PARTNER_CENTER_UTC; import java.time.Instant; +import java.util.Objects; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.social.partnercenter.PartnerCenter; import org.springframework.social.partnercenter.api.AbstractTemplate; @@ -47,7 +50,8 @@ public Boolean isDomainAvailable(String domain) { return !restResource.request() .pathSegment("domains", domain) .noRetry() - .head().getStatusCode().equals(OK); + .head().getStatusCode() + .equals(OK); } catch (ApiFaultException fault){ return fault.getHttpStatus().equals(NOT_FOUND); } @@ -55,9 +59,17 @@ public Boolean isDomainAvailable(String domain) { @Override public ResponseEntity validateAddress(Address address) { - return restResource.request() - .pathSegment("validations", "address") - .post(address, Boolean.class); + try { + return restResource.request() + .pathSegment("validations", "address") + .post(address, Boolean.class); + } catch (ApiFaultException fault) { + if (Objects.equals(fault.getStatusCode(), HttpStatus.BAD_REQUEST)) { + return ok(false); + } else { + throw fault; + } + } } @Override diff --git a/src/main/java/org/springframework/social/partnercenter/security/AzureADAuthTemplate.java b/src/main/java/org/springframework/social/partnercenter/security/AzureADAuthTemplate.java index bde4c44..6f38953 100644 --- a/src/main/java/org/springframework/social/partnercenter/security/AzureADAuthTemplate.java +++ b/src/main/java/org/springframework/social/partnercenter/security/AzureADAuthTemplate.java @@ -4,8 +4,6 @@ import static org.springframework.social.partnercenter.api.uri.UriProvider.DEFAULT_URL_PROVIDER; import static org.springframework.util.Assert.notNull; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Map; @@ -19,7 +17,6 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.social.oauth2.AccessGrant; -import org.springframework.social.oauth2.GrantType; import org.springframework.social.partnercenter.api.ApiAuthorizationException; import org.springframework.social.partnercenter.api.AuthorizationFault; import org.springframework.social.partnercenter.api.uri.UriProvider; @@ -32,7 +29,7 @@ import org.springframework.social.support.FormMapHttpMessageConverter; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; public class AzureADAuthTemplate implements AzureADAuthOperations { @@ -136,7 +133,7 @@ public AccessGrant exchangeForAccess(String authorizationCode, MultiValueMap result) { return createAccessGrant((String) result.get("access_token"), (String) result.get("scope"), (String) result.get("refresh_token"), getIntegerValue(result, "expires_in"), (String) result.get("id_token"), result); } - private String formEncode(String data) { - try { - return URLEncoder.encode(data, "UTF-8"); - } - catch (UnsupportedEncodingException ex) { - // should not happen, UTF-8 is always supported - throw new IllegalStateException(ex); - } - } - // Retrieves object from map into an Integer, regardless of the object's actual type. Allows for flexibility in object type (eg, "3600" vs 3600). private Long getIntegerValue(Map map, String key) { try { @@ -319,7 +310,7 @@ private Long getIntegerValue(Map map, String key) { } } - private ApiAuthorizationException buildAuthFault(HttpClientErrorException e){ + private ApiAuthorizationException buildAuthFault(HttpStatusCodeException e){ String responseBody = e.getResponseBodyAsString(); try { AuthorizationFault authorizationFault = Json.fromJson(responseBody, AuthorizationFault.class); @@ -331,9 +322,6 @@ private ApiAuthorizationException buildAuthFault(HttpClientErrorException e){ } } - private PartnerCenterGrantType convertGrantType(GrantType grantType){ - return grantType.equals(GrantType.AUTHORIZATION_CODE) ? PartnerCenterGrantType.JWT_TOKEN : PartnerCenterGrantType.CLIENT_CREDENTIALS; - } }