use of io.github.microcks.domain.Exchange in project microcks by microcks.
the class ServiceChangeEventPublisher method onApplicationEvent.
@Override
@Async
public void onApplicationEvent(ServiceChangeEvent event) {
log.debug("Received a ServiceChangeEvent on " + event.getServiceId());
ServiceView serviceView = null;
if (event.getChangeType() != ChangeType.DELETED) {
Service service = serviceRepository.findById(event.getServiceId()).orElse(null);
if (service != null) {
// Put messages into a map where key is operation name.
Map<String, List<? extends Exchange>> messagesMap = new HashMap<>();
for (Operation operation : service.getOperations()) {
if (service.getType() == ServiceType.EVENT) {
// If an event, we should explicitly retrieve event messages.
List<UnidirectionalEvent> events = messageService.getEventByOperation(IdBuilder.buildOperationId(service, operation));
messagesMap.put(operation.getName(), events);
} else {
// Otherwise we have traditional request / response pairs.
List<RequestResponsePair> pairs = messageService.getRequestResponseByOperation(IdBuilder.buildOperationId(service, operation));
messagesMap.put(operation.getName(), pairs);
}
}
serviceView = new ServiceView(service, messagesMap);
}
}
// Build and send a ServiceViewChangeEvent that wraps ServiceView.
ServiceViewChangeEvent serviceViewChangeEvent = new ServiceViewChangeEvent(event.getServiceId(), serviceView, event.getChangeType(), System.currentTimeMillis());
kafkaTemplate.send("microcks-services-updates", event.getServiceId(), serviceViewChangeEvent);
log.debug("Processing of ServiceChangeEvent done !");
}
use of io.github.microcks.domain.Exchange in project microcks by microcks.
the class PostmanCollectionImporter method getMessageDefinitionsV2.
private List<Exchange> getMessageDefinitionsV2(Service service, Operation operation) {
Map<Request, Response> result = new HashMap<Request, Response>();
Iterator<JsonNode> items = collection.path("item").elements();
while (items.hasNext()) {
JsonNode item = items.next();
result.putAll(getMessageDefinitionsV2("", item, operation));
}
// Adapt map to list of Exchanges.
return result.entrySet().stream().map(entry -> new RequestResponsePair(entry.getKey(), entry.getValue())).collect(Collectors.toList());
}
use of io.github.microcks.domain.Exchange in project microcks by microcks.
the class AsyncAPIImporter method getMessageDefinitions.
@Override
public List<Exchange> getMessageDefinitions(Service service, Operation operation) throws MockRepositoryImportException {
List<Exchange> result = new ArrayList<>();
// Retrieve default content type, defaulting to application/json.
String defaultContentType = "application/json";
if (spec.has("defaultContentType")) {
defaultContentType = spec.get("defaultContentType").asText("application/json");
}
// Iterate on specification "channels" nodes.
Iterator<Entry<String, JsonNode>> channels = spec.path("channels").fields();
while (channels.hasNext()) {
Entry<String, JsonNode> channel = channels.next();
String channelName = channel.getKey();
Map<String, Map<String, String>> pathParametersByExample = extractParametersByExample(channel.getValue());
// Iterate on specification path, "verbs" nodes.
Iterator<Entry<String, JsonNode>> verbs = channel.getValue().fields();
while (verbs.hasNext()) {
Entry<String, JsonNode> verb = verbs.next();
String verbName = verb.getKey();
// Find the correct operation.
if (operation.getName().equals(verbName.toUpperCase() + " " + channelName.trim())) {
JsonNode messageBody = verb.getValue().path("message");
// If it's a $ref or multi-structure (oneOf, anyOf, allOf), then navigate to them.
List<JsonNode> messageBodies = followRefsIfAny(messageBody);
for (JsonNode extractedMsgBody : messageBodies) {
// Get message content type.
String contentType = defaultContentType;
if (extractedMsgBody.has("contentType")) {
contentType = extractedMsgBody.path("contentType").asText();
}
// No need to go further if no examples.
if (extractedMsgBody.has("examples")) {
Iterator<JsonNode> examples = extractedMsgBody.path("examples").elements();
int exampleIndex = 0;
while (examples.hasNext()) {
JsonNode exampleNode = examples.next();
EventMessage eventMessage = null;
if (exampleNode.has("name")) {
// As of AsyncAPI 2.1.0 () we can now have a 'name' property for examples!
eventMessage = extractFromAsyncAPI21Example(contentType, exampleNode);
} else if (exampleNode.has("payload")) {
// As of https://github.com/microcks/microcks/issues/385, we should support the restriction
// coming from AsyncAPI GItHub master revision and associated tooling...
eventMessage = extractFromAsyncAPIExample(contentType, exampleNode, channelName.trim() + "-" + exampleIndex);
} else {
eventMessage = extractFromMicrocksExample(contentType, exampleNode);
}
// add this event message as a valid event in results exchanges.
if (eventMessage != null) {
if (DispatchStyles.URI_PARTS.equals(operation.getDispatcher())) {
String resourcePathPattern = channelName;
Map<String, String> parts = pathParametersByExample.get(eventMessage.getName());
String resourcePath = URIBuilder.buildURIFromPattern(resourcePathPattern, parts);
operation.addResourcePath(resourcePath);
eventMessage.setDispatchCriteria(DispatchCriteriaHelper.buildFromPartsMap(parts));
}
result.add(new UnidirectionalEvent(eventMessage));
}
exampleIndex++;
}
}
}
}
}
}
return result;
}
use of io.github.microcks.domain.Exchange in project microcks by microcks.
the class ServiceController method getService.
@RequestMapping(value = "/services/{id:.+}", method = RequestMethod.GET)
public ResponseEntity<?> getService(@PathVariable("id") String serviceId, @RequestParam(value = "messages", required = false, defaultValue = "true") boolean messages) {
log.debug("Retrieving service with id {}", serviceId);
Service service = null;
// serviceId may have the form of <service_name>:<service_version>
if (serviceId.contains(":")) {
String name = serviceId.substring(0, serviceId.indexOf(':'));
String version = serviceId.substring(serviceId.indexOf(':') + 1);
// If service name was encoded with '+' instead of '%20', replace them.
if (name.contains("+")) {
name = name.replace('+', ' ');
}
service = serviceRepository.findByNameAndVersion(name, version);
} else {
service = serviceRepository.findById(serviceId).orElse(null);
}
if (messages) {
// Put messages into a map where key is operation name.
Map<String, List<? extends Exchange>> messagesMap = new HashMap<>();
for (Operation operation : service.getOperations()) {
if (service.getType() == ServiceType.EVENT) {
// If an event, we should explicitly retrieve event messages.
List<UnidirectionalEvent> events = messageService.getEventByOperation(IdBuilder.buildOperationId(service, operation));
messagesMap.put(operation.getName(), events);
} else {
// Otherwise we have traditional request / response pairs.
List<RequestResponsePair> pairs = messageService.getRequestResponseByOperation(IdBuilder.buildOperationId(service, operation));
messagesMap.put(operation.getName(), pairs);
}
}
return new ResponseEntity<>(new ServiceView(service, messagesMap), HttpStatus.OK);
}
return new ResponseEntity<>(service, HttpStatus.OK);
}
use of io.github.microcks.domain.Exchange in project microcks by microcks.
the class ServiceService method importServiceDefinition.
/**
* Import definitions of services and bounded resources and messages into Microcks
* repository. This uses a MockRepositoryImporter under hood.
* @param repositoryFile The File for mock repository.
* @param referenceResolver The Resolver to be used during import (may be null).
* @param artifactInfo The essential information on Artifact to import.
* @return The list of imported Services
* @throws MockRepositoryImportException if something goes wrong (URL not reachable nor readable, etc...)
*/
public List<Service> importServiceDefinition(File repositoryFile, ReferenceResolver referenceResolver, ArtifactInfo artifactInfo) throws MockRepositoryImportException {
// Retrieve the correct importer based on file path.
MockRepositoryImporter importer = null;
try {
importer = MockRepositoryImporterFactory.getMockRepositoryImporter(repositoryFile, referenceResolver);
} catch (IOException ioe) {
log.error("Exception while accessing file " + repositoryFile.getPath(), ioe);
throw new MockRepositoryImportException(ioe.getMessage(), ioe);
}
Service reference = null;
boolean serviceUpdate = false;
List<Service> services = importer.getServiceDefinitions();
for (Service service : services) {
Service existingService = serviceRepository.findByNameAndVersion(service.getName(), service.getVersion());
log.debug("Service [{}, {}] exists ? {}", service.getName(), service.getVersion(), existingService != null);
// If it's the main artifact: retrieve previous id and props if update, save anyway.
if (artifactInfo.isMainArtifact()) {
if (existingService != null) {
// Retrieve its previous identifier and metadatas
// (backup metadata that may have been imported with extensions).
Metadata backup = service.getMetadata();
service.setId(existingService.getId());
service.setMetadata(existingService.getMetadata());
// If there was metadata found through extensions, overwrite historical ones.
if (backup != null) {
existingService.getMetadata().setLabels(backup.getLabels());
existingService.getMetadata().setAnnotations(backup.getAnnotations());
}
// Keep its overriden operation properties.
copyOverridenOperations(existingService, service);
serviceUpdate = true;
}
if (service.getMetadata() == null) {
service.setMetadata(new Metadata());
}
// For services of type EVENT, we should put default values on frequency and bindings.
if (service.getType().equals(ServiceType.EVENT)) {
manageEventServiceDefaults(service);
}
service.getMetadata().objectUpdated();
service.setSourceArtifact(artifactInfo.getArtifactName());
service = serviceRepository.save(service);
// We're dealing with main artifact so reference is saved or updated one.
reference = service;
} else {
// It's a secondary artifact just for messages or metadata. We'll have problems if not having an existing service...
if (existingService == null) {
log.warn("Trying to import {} as a secondary artifact but there's no existing [{}, {}] Service. Just skipping.", artifactInfo.getArtifactName(), service.getName(), service.getVersion());
break;
}
// update the existing service with them.
if (service.getMetadata() != null) {
existingService.getMetadata().setLabels(service.getMetadata().getLabels());
existingService.getMetadata().setAnnotations(service.getMetadata().getAnnotations());
}
for (Operation operation : service.getOperations()) {
Operation existingOp = existingService.getOperations().stream().filter(op -> op.getName().equals(operation.getName())).findFirst().orElse(null);
if (existingOp != null) {
if (operation.getDefaultDelay() != null) {
existingOp.setDefaultDelay(operation.getDefaultDelay());
}
if (operation.getDispatcher() != null) {
existingOp.setDispatcher(operation.getDispatcher());
}
if (operation.getDispatcherRules() != null) {
existingOp.setDispatcherRules(operation.getDispatcherRules());
}
}
}
// We're dealing with secondary artifact so reference is the pre-existing one.
// Moreover, we should replace current imported service (unbound/unsaved)
// by reference in the results list.
reference = existingService;
services.remove(service);
services.add(reference);
}
// Remove resources previously attached to service.
List<Resource> existingResources = resourceRepository.findByServiceIdAndSourceArtifact(reference.getId(), artifactInfo.getArtifactName());
if (existingResources != null && existingResources.size() > 0) {
resourceRepository.deleteAll(existingResources);
}
// Save new resources.
List<Resource> resources = importer.getResourceDefinitions(service);
for (Resource resource : resources) {
resource.setServiceId(reference.getId());
resource.setSourceArtifact(artifactInfo.getArtifactName());
}
resourceRepository.saveAll(resources);
for (Operation operation : reference.getOperations()) {
String operationId = IdBuilder.buildOperationId(reference, operation);
// Remove messages previously attached to service.
requestRepository.deleteAll(requestRepository.findByOperationIdAndSourceArtifact(operationId, artifactInfo.getArtifactName()));
responseRepository.deleteAll(responseRepository.findByOperationIdAndSourceArtifact(operationId, artifactInfo.getArtifactName()));
eventMessageRepository.deleteAll(eventMessageRepository.findByOperationIdAndSourceArtifact(operationId, artifactInfo.getArtifactName()));
List<Exchange> exchanges = importer.getMessageDefinitions(service, operation);
for (Exchange exchange : exchanges) {
if (exchange instanceof RequestResponsePair) {
RequestResponsePair pair = (RequestResponsePair) exchange;
// Associate request and response with operation and artifact.
pair.getRequest().setOperationId(operationId);
pair.getResponse().setOperationId(operationId);
pair.getRequest().setSourceArtifact(artifactInfo.getArtifactName());
pair.getResponse().setSourceArtifact(artifactInfo.getArtifactName());
// Save response and associate request with response before saving it.
responseRepository.save(pair.getResponse());
pair.getRequest().setResponseId(pair.getResponse().getId());
requestRepository.save(pair.getRequest());
} else if (exchange instanceof UnidirectionalEvent) {
UnidirectionalEvent event = (UnidirectionalEvent) exchange;
// Associate event message with operation and artifact before saving it..
event.getEventMessage().setOperationId(operationId);
event.getEventMessage().setSourceArtifact(artifactInfo.getArtifactName());
eventMessageRepository.save(event.getEventMessage());
}
}
}
// When extracting message information, we may have modified Operation because discovered new resource paths
// depending on variable URI parts. As a consequence, we got to update Service in repository.
serviceRepository.save(reference);
// Publish a Service update event before returning.
publishServiceChangeEvent(reference, serviceUpdate ? ChangeType.UPDATED : ChangeType.CREATED);
}
log.info("Having imported {} services definitions into repository", services.size());
return services;
}
Aggregations