diff options
author | NicolasLaplaud <nicolas.laplaud@orange.com> | 2018-07-13 11:08:16 +0200 |
---|---|---|
committer | Quoc-Nghia Nguyen <quocnghia.nguyen@orange.com> | 2018-07-13 11:52:59 +0200 |
commit | d46f9e5e04452a2cd66d1260e1f28ed6e9aea429 (patch) | |
tree | 73273648699e807f2d29fc803439cc5da6fd7123 /src/main/java/org/onap | |
parent | 0e3e6d0a87f7c8dcc998fc57b4c04d49c7a56a76 (diff) |
HUB Resource
- adding SubscriberNotifier based on event types
- storing subscriptions
Change-Id: I5729717699f6e903de4c541c43951e2d2d56cea7
Issue-ID: EXTAPI-96
Signed-off-by: NicolasLaplaud <nicolas.laplaud@orange.com>
Signed-off-by: Quoc-Nghia Nguyen <quocnghia.nguyen@orange.com>
Diffstat (limited to 'src/main/java/org/onap')
15 files changed, 456 insertions, 61 deletions
diff --git a/src/main/java/org/onap/nbi/Application.java b/src/main/java/org/onap/nbi/Application.java index 6a9bcbe..05588b4 100644 --- a/src/main/java/org/onap/nbi/Application.java +++ b/src/main/java/org/onap/nbi/Application.java @@ -1,24 +1,26 @@ /** - * Copyright (c) 2018 Orange - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2018 Orange + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.onap.nbi; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication +@EnableAsync public class Application { public static void main(String[] args) { diff --git a/src/main/java/org/onap/nbi/apis/hub/HubConfig.java b/src/main/java/org/onap/nbi/apis/hub/HubConfig.java new file mode 100644 index 0000000..693e628 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/HubConfig.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub; + +import org.onap.nbi.apis.hub.service.CriteriaBuilder; +import org.onap.nbi.apis.hub.service.CriteriaBuilderServiceOrder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class HubConfig { + @Bean + CriteriaBuilder criteriaBuilder() { + return new CriteriaBuilderServiceOrder(); + } +} diff --git a/src/main/java/org/onap/nbi/apis/hub/HubResource.java b/src/main/java/org/onap/nbi/apis/hub/HubResource.java index 491ff75..54e9001 100644..100755 --- a/src/main/java/org/onap/nbi/apis/hub/HubResource.java +++ b/src/main/java/org/onap/nbi/apis/hub/HubResource.java @@ -15,8 +15,11 @@ */ package org.onap.nbi.apis.hub; -import org.onap.nbi.apis.hub.model.EventSubscription; -import org.onap.nbi.apis.hub.repository.EventSubscriptionRepository; +import org.onap.nbi.apis.hub.model.Subscriber; +import org.onap.nbi.apis.hub.model.Subscription; +import org.onap.nbi.apis.hub.repository.SubscriberRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -34,16 +37,22 @@ import java.net.URI; @EnableScheduling public class HubResource { + Logger logger = LoggerFactory.getLogger(HubResource.class); + @Autowired - EventSubscriptionRepository eventSubscriptionRepository; + SubscriberRepository subscriberRepository; @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<Object> createEventSubscription(@RequestBody EventSubscription eventSubscription) { - EventSubscription result = eventSubscriptionRepository.save(eventSubscription); + public ResponseEntity<Subscriber> createEventSubscription(@RequestBody Subscription subscription) { + logger.debug("Received subscription request: {}", subscription); + + Subscriber sub = Subscriber.createFromRequest(subscription); + sub = subscriberRepository.save(sub); + URI location = ServletUriComponentsBuilder .fromCurrentRequest() .path("{id}") - .buildAndExpand(result.getId()) + .buildAndExpand(sub.getId()) .toUri(); return ResponseEntity.created(location).build(); diff --git a/src/main/java/org/onap/nbi/apis/hub/model/Event.java b/src/main/java/org/onap/nbi/apis/hub/model/Event.java new file mode 100755 index 0000000..e68d322 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/model/Event.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.model; + +import com.fasterxml.jackson.databind.JsonNode; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + + +public class Event { + + private String eventId; + private LocalDateTime eventDate; + + @NotNull + private String eventType; + + @NotNull + private JsonNode event; + + public String getEventId() { + return eventId; + } + + public void setEventId(String eventId) { + this.eventId = eventId; + } + + public LocalDateTime getEventDate() { + return eventDate; + } + + public void setEventDate(LocalDateTime eventDate) { + this.eventDate = eventDate; + } + + public String getEventType() { + return eventType; + } + + public void setEventType(String eventType) { + this.eventType = eventType; + } + + public JsonNode getEvent() { + return event; + } + + public void setEvent(JsonNode event) { + this.event = event; + } +} diff --git a/src/main/java/org/onap/nbi/apis/hub/model/Subscriber.java b/src/main/java/org/onap/nbi/apis/hub/model/Subscriber.java new file mode 100755 index 0000000..a18833e --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/model/Subscriber.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.model; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + + +@Document +public class Subscriber { + private static final Logger logger = LoggerFactory.getLogger(Subscriber.class); + + @Id + private String id; + private String callback; + + private Map<String, String[]> query = new HashMap<>(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCallback() { + return callback; + } + + public void setCallback(String callback) { + this.callback = callback; + } + + public Map<String, String[]> getQuery() { + return query; + } + + public static Subscriber createFromRequest(Subscription request) { + Subscriber sub = new Subscriber(); + sub.setCallback(request.getCallback()); + + Stream.of(request.getQuery().split("&")) + .map(q -> q.split("=")) + .filter(q -> q.length == 2) + .forEach(q -> sub.getQuery().put(q[0], q[1].split(","))); + + return sub; + } +} diff --git a/src/main/java/org/onap/nbi/apis/hub/model/EventSubscription.java b/src/main/java/org/onap/nbi/apis/hub/model/Subscription.java index 3911c23..70b50b4 100644..100755 --- a/src/main/java/org/onap/nbi/apis/hub/model/EventSubscription.java +++ b/src/main/java/org/onap/nbi/apis/hub/model/Subscription.java @@ -15,41 +15,22 @@ */ package org.onap.nbi.apis.hub.model; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - import java.util.Objects; +public class Subscription { -@Document -public class EventSubscription { - - @Id - private String id; private String callback; private String query; - private String eventType; - - public EventSubscription(){ + public Subscription(){ } - public EventSubscription(String id, String callback, String query, String eventType) { - this.id = id; + public Subscription(String callback, String query) { this.callback = callback; this.query = query; - this.eventType = eventType; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; } public String getCallback() { @@ -68,28 +49,18 @@ public class EventSubscription { this.query = query; } - public String getEventType() { - return eventType; - } - - public void setEventType(String eventType) { - this.eventType = eventType; - } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - EventSubscription that = (EventSubscription) o; - return Objects.equals(id, that.id) && - Objects.equals(callback, that.callback) && - Objects.equals(query, that.query) && - Objects.equals(eventType, that.eventType); + Subscription that = (Subscription) o; + return Objects.equals(callback, that.callback) && + Objects.equals(query, that.query); } @Override public int hashCode() { - return Objects.hash(id, callback, query, eventType); + return Objects.hash(callback, query); } } diff --git a/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberFinder.java b/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberFinder.java new file mode 100755 index 0000000..ab23a3c --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberFinder.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.repository; + +import org.onap.nbi.apis.hub.model.Event; +import org.onap.nbi.apis.hub.model.Subscriber; + +import java.util.List; + +public interface SubscriberFinder { + List<Subscriber> findSubscribersUsingEvent(Event event); +} diff --git a/src/main/java/org/onap/nbi/apis/hub/repository/EventSubscriptionRepository.java b/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberRepository.java index 3d5fc88..b80effb 100644..100755 --- a/src/main/java/org/onap/nbi/apis/hub/repository/EventSubscriptionRepository.java +++ b/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberRepository.java @@ -15,8 +15,10 @@ */ package org.onap.nbi.apis.hub.repository; -import org.onap.nbi.apis.hub.model.EventSubscription; +import org.onap.nbi.apis.hub.model.Subscriber; import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; -public interface EventSubscriptionRepository extends MongoRepository<EventSubscription, String> { +@Repository +public interface SubscriberRepository extends SubscriberFinder, MongoRepository<Subscriber, String> { } diff --git a/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberRepositoryImpl.java b/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberRepositoryImpl.java new file mode 100755 index 0000000..9799e48 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/repository/SubscriberRepositoryImpl.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.repository; + +import org.onap.nbi.apis.hub.model.Event; +import org.onap.nbi.apis.hub.model.Subscriber; +import org.onap.nbi.apis.hub.service.CriteriaBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; + +import java.util.List; + +public class SubscriberRepositoryImpl implements SubscriberFinder { + @Autowired + private MongoOperations mongoOperations; + + @Autowired + private CriteriaBuilder criteriaBuilder; + + @Override + public List<Subscriber> findSubscribersUsingEvent(Event event) { + Criteria criteria = new Criteria(); + criteria.and("query.eventType").is(event.getEventType()); + criteriaBuilder.adjust(criteria, event); + return mongoOperations.find(new Query(criteria), Subscriber.class); + } +} diff --git a/src/main/java/org/onap/nbi/apis/hub/service/CriteriaBuilder.java b/src/main/java/org/onap/nbi/apis/hub/service/CriteriaBuilder.java new file mode 100755 index 0000000..4d4872d --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/service/CriteriaBuilder.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.service; + +import org.onap.nbi.apis.hub.model.Event; +import org.springframework.data.mongodb.core.query.Criteria; + +public interface CriteriaBuilder { + Criteria adjust(Criteria base, Event event); +} diff --git a/src/main/java/org/onap/nbi/apis/hub/service/CriteriaBuilderServiceOrder.java b/src/main/java/org/onap/nbi/apis/hub/service/CriteriaBuilderServiceOrder.java new file mode 100755 index 0000000..31f0839 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/service/CriteriaBuilderServiceOrder.java @@ -0,0 +1,71 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.service; + +import com.fasterxml.jackson.databind.JsonNode; +import org.onap.nbi.apis.hub.model.Event; +import org.springframework.data.mongodb.core.query.Criteria; + +import java.util.ArrayList; +import java.util.List; + +public class CriteriaBuilderServiceOrder implements CriteriaBuilder { + @Override + public Criteria adjust(Criteria base, Event event) { + switch (event.getEventType()) { + case "ServiceOrderCreationNotification": + return base; + + case "ServiceOrderStateChangeNotification": + JsonNode stateNode = event.getEvent().path("state"); + if (stateNode.isValueNode()) + return base.orOperator( + Criteria.where("query.serviceOrder__state").exists(false), + Criteria.where("query.serviceOrder__state").in(event.getEvent().path("state").textValue()) + ); + else + return base.and("query.serviceOrder__state").exists(false); + + case "ServiceOrderItemStateChangeNotification": + Object[] states = getStates(event); + if (states.length > 0) + return base.orOperator( + Criteria.where("query.serviceOrder__serviceOrderItem__state").exists(false), + Criteria.where("query.serviceOrder__serviceOrderItem__state").in(states) + ); + else + return base.and("query.serviceOrder__serviceOrderItem__state").exists(false); + } + + return base; + } + + private String[] getStates(Event event) { + List<String> states = new ArrayList<>(); + + JsonNode orderItems = event.getEvent().path("orderItem"); + if (orderItems.isArray()) { + for (JsonNode node : orderItems) { + JsonNode stateNode = node.path("state"); + if (stateNode.isValueNode()) { + states.add(stateNode.textValue()); + } + } + } + + return states.toArray(new String[0]); + } +} diff --git a/src/main/java/org/onap/nbi/apis/hub/MyAspect.java b/src/main/java/org/onap/nbi/apis/hub/service/NotificationAspect.java index 3f0f99b..ad7ab81 100644..100755 --- a/src/main/java/org/onap/nbi/apis/hub/MyAspect.java +++ b/src/main/java/org/onap/nbi/apis/hub/service/NotificationAspect.java @@ -13,26 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.onap.nbi.apis.hub; +package org.onap.nbi.apis.hub.service; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; +import org.onap.nbi.apis.hub.repository.SubscriberRepository; import org.onap.nbi.apis.serviceorder.model.ServiceOrder; import org.onap.nbi.apis.serviceorder.model.StateType; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.stereotype.Component; @Aspect @Component @Configurable -public class MyAspect { +public class NotificationAspect { + + @Autowired + private SubscriberRepository subscriberRepository; + + @Autowired + private NotifierService notifier; @AfterReturning(value = "execution(* org.onap.nbi.apis.serviceorder.service.ServiceOrderService" + ".createServiceOrder(..))", returning = "serviceOrderCreated") public void whenCreateServiceOrder(ServiceOrder serviceOrderCreated) { if(StateType.ACKNOWLEDGED.equals(serviceOrderCreated.getState())) { - // Notif createServiceOrder + // Notif createServiceOrder } } diff --git a/src/main/java/org/onap/nbi/apis/hub/service/NotifierService.java b/src/main/java/org/onap/nbi/apis/hub/service/NotifierService.java new file mode 100755 index 0000000..2bfbca1 --- /dev/null +++ b/src/main/java/org/onap/nbi/apis/hub/service/NotifierService.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2018 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.apis.hub.service; + +import org.onap.nbi.apis.hub.model.Event; +import org.onap.nbi.apis.hub.model.Subscriber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.validation.Valid; + +@Service +public class NotifierService { + private final Logger logger = LoggerFactory.getLogger(NotifierService.class); + + @Async + public void run(Subscriber subscriber, @Valid Event event) { + ResponseEntity<String> re = new RestTemplate().postForEntity(subscriber.getCallback(), event, String.class); + if (re.getStatusCode() == HttpStatus.OK) logger.debug("FAILED"); + } +} diff --git a/src/main/java/org/onap/nbi/configuration/MongoConfig.java b/src/main/java/org/onap/nbi/configuration/MongoConfig.java new file mode 100755 index 0000000..f4e78dc --- /dev/null +++ b/src/main/java/org/onap/nbi/configuration/MongoConfig.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2018 Orange + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onap.nbi.configuration; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.core.convert.MappingMongoConverter; + +@Configuration +public class MongoConfig implements BeanPostProcessor { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof MappingMongoConverter) { + MappingMongoConverter converter = (MappingMongoConverter) bean; + converter.setMapKeyDotReplacement("__"); + } + return bean; + } + + @Override + public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { + return o; + } +}
\ No newline at end of file diff --git a/src/main/java/org/onap/nbi/apis/RestConfiguration.java b/src/main/java/org/onap/nbi/configuration/RestConfiguration.java index d9e3206..835ce24 100644 --- a/src/main/java/org/onap/nbi/apis/RestConfiguration.java +++ b/src/main/java/org/onap/nbi/configuration/RestConfiguration.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.onap.nbi.apis; +package org.onap.nbi.configuration; import org.onap.nbi.exceptions.BackendErrorHandler; import org.springframework.boot.web.client.RestTemplateBuilder; |