How to use GraciousProcessingReport class of com.consol.citrus.validation.json.report package

Best Citrus code snippet using com.consol.citrus.validation.json.report.GraciousProcessingReport

Source:GraciousProcessingReport.java Github

copy

Full Screen

...27 * This class implements a report that represents a gracious interpretation of a set of processing reports28 * generated by the json-schema-validation library.29 * @since 2.7.330 */31public class GraciousProcessingReport implements ProcessingReport {32 /**33 * The highest log level seen so far34 */35 private LogLevel currentLevel = LogLevel.DEBUG;36 /**37 * The log threshold38 */39 private LogLevel logLevel = LogLevel.INFO;40 /**41 * The exception threshold42 */43 private LogLevel exceptionThreshold = LogLevel.FATAL;44 private boolean success;45 private final List<ProcessingMessage> messages = new ArrayList<>();46 /**47 * Constructor to determine the default success state oft he Report48 * @param success the default success state49 */50 public GraciousProcessingReport(boolean success) {51 this.success = success;52 }53 /**54 * Creates a GraciousProcessingReport while preserving the information from the given list of ProcessingReports55 * @param processingReports The list of reports to merge with the new GraciousProcessingReport56 */57 public GraciousProcessingReport(List<ProcessingReport> processingReports) {58 this(false);59 for (ProcessingReport processingReport : processingReports) {60 mergeWith(processingReport);61 }62 }63 @Override64 public void mergeWith(final ProcessingReport processingReport) {65 success = success || processingReport.isSuccess();66 processingReport.forEach(messages::add);67 }68 @Override69 public LogLevel getLogLevel() {70 return logLevel;71 }...

Full Screen

Full Screen

Source:JsonSchemaValidation.java Github

copy

Full Screen

...18import com.consol.citrus.json.JsonSchemaRepository;19import com.consol.citrus.json.schema.SimpleJsonSchema;20import com.consol.citrus.message.Message;21import com.consol.citrus.validation.json.JsonMessageValidationContext;22import com.consol.citrus.validation.json.report.GraciousProcessingReport;23import com.fasterxml.jackson.databind.JsonNode;24import com.fasterxml.jackson.databind.ObjectMapper;25import com.github.fge.jsonschema.core.exceptions.ProcessingException;26import com.github.fge.jsonschema.core.report.ProcessingReport;27import org.springframework.context.ApplicationContext;28import java.io.IOException;29import java.util.LinkedList;30import java.util.List;31/**32 * This class is responsible for the validation of json messages against json schemas / json schema repositories.33 * @since 2.7.334 */35public class JsonSchemaValidation {36 private final JsonSchemaFilter jsonSchemaFilter;37 /** Object Mapper to convert the message for validation*/38 private ObjectMapper objectMapper = new ObjectMapper();39 /**40 * Default constructor using default filter.41 */42 public JsonSchemaValidation() {43 this(new JsonSchemaFilter());44 }45 /**46 * Constructor using filter implementation.47 * @param jsonSchemaFilter48 */49 public JsonSchemaValidation(JsonSchemaFilter jsonSchemaFilter) {50 this.jsonSchemaFilter = jsonSchemaFilter;51 }52 /**53 * Validates the given message against a list of JsonSchemaRepositories under consideration of the actual context54 * @param message The message to be validated55 * @param schemaRepositories The schema repositories to be used for validation56 * @param validationContext The context of the validation to be used for the validation57 * @param applicationContext The application context to be used for the validation58 * @return A report holding the results of the validation59 */60 public ProcessingReport validate(Message message,61 List<JsonSchemaRepository> schemaRepositories,62 JsonMessageValidationContext validationContext,63 ApplicationContext applicationContext) {64 return validate(message, jsonSchemaFilter.filter(schemaRepositories, validationContext, applicationContext));65 }66 /**67 * Validates a message against all schemas contained in the given json schema repository68 * @param message The message to be validated69 * @param jsonSchemas The list of json schemas to iterate over70 */71 private GraciousProcessingReport validate(Message message, List<SimpleJsonSchema> jsonSchemas) {72 if (jsonSchemas.isEmpty()) {73 return new GraciousProcessingReport(true);74 } else {75 List<ProcessingReport> processingReports = new LinkedList<>();76 for (SimpleJsonSchema simpleJsonSchema : jsonSchemas) {77 processingReports.add(validate(message, simpleJsonSchema));78 }79 return new GraciousProcessingReport(processingReports);80 }81 }82 /**83 * Validates a given message against a given json schema84 * @param message The message to be validated85 * @param simpleJsonSchema The json schema to validate against86 * @return returns the report holding the result of the validation87 */88 private ProcessingReport validate(Message message, SimpleJsonSchema simpleJsonSchema) {89 try {90 JsonNode receivedJson = objectMapper.readTree(message.getPayload(String.class));91 return simpleJsonSchema.getSchema().validate(receivedJson);92 } catch (IOException | ProcessingException e) {93 throw new CitrusRuntimeException("Failed to validate Json schema", e);...

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1package com.consol.citrus.validation.json.report;2import com.consol.citrus.context.TestContext;3import com.consol.citrus.exceptions.CitrusRuntimeException;4import com.consol.citrus.message.Message;5import com.consol.citrus.validation.json.JsonPathMessageValidator;6import com.consol.citrus.validation.json.JsonValidationContext;7import com.consol.citrus.validation.json.JsonValidationResult;8import com.consol.citrus.validation.json.JsonValidationUtils;9import com.consol.citrus.validation.json.report.GraciousProcessingReport;10import org.everit.json.schema.ValidationException;11import org.everit.json.schema.loader.SchemaLoader;12import org.json.JSONArray;13import org.json.JSONObject;14import org.json.JSONTokener;15import org.slf4j.Logger;16import org.slf4j.LoggerFactory;17import org.springframework.core.io.Resource;18import java.io.IOException;19import java.io.InputStream;20import java.util.List;21import java.util.Map.Entry;22public class JsonPathMessageValidator extends JsonMessageValidator {23 private static final Logger LOG = LoggerFactory.getLogger(JsonPathMessageValidator.class);24 private final Resource schemaResource;25 private final String schemaDefinition;26 private final String schemaDefinitionPath;27 private final String schemaDefinitionValue;28 public JsonPathMessageValidator(Resource schemaResource, String schemaDefinition, String schemaDefinitionPath, String schemaDefinitionValue) {29 this.schemaResource = schemaResource;30 this.schemaDefinition = schemaDefinition;31 this.schemaDefinitionPath = schemaDefinitionPath;32 this.schemaDefinitionValue = schemaDefinitionValue;33 }34 public JsonValidationResult validateMessage(Message receivedMessage, Message controlMessage, TestContext context) {35 JsonValidationResult validationResult = new JsonValidationResult();36 JSONObject jsonSchema = getJsonSchema(context);37 JSONObject receivedJson = getReceivedJson(receivedMessage, context);38 if (LOG.isDebugEnabled()) {

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1import com.consol.citrus.validation.json.JsonMessageValidationContext;2import com.consol.citrus.validation.json.report.GraciousProcessingReport;3import com.consol.citrus.validation.json.JsonMessageValidator;4import com.consol.citrus.validation.json.JsonSchemaValidationContext;5import com.consol.citrus.validation.json.JsonValidationContext;6import com.consol.citrus.validation.json.JsonValidationProcessor;7import com.consol.citrus.validation.json.JsonValidationUtils;8import com.consol.citrus.validation.json.JsonSchemaValidationContext.SchemaValidationType;9import com.consol.citrus.validation.json.JsonValidationContext.ValidationType;10import com.consol.citrus.validation.json.report.GraciousProcessingReport;11import com.consol.citrus.validation.json.JsonMessageValidationContext;12import com.consol.citrus.message.Message;13import com.consol.citrus.message.MessageType;14import com.consol.citrus.message.MessageHeaders;15import com.consol.citrus.validation.DefaultValidationContext;16import com.consol.citrus.validation.DefaultValidationContextFactory;17import com.consol.citrus.validation.MessageValidator;18import com.consol.citrus.validation.ValidationContext;19import com.consol.citrus.validation.ValidationContextFactory;20import com.consol.citrus.validation.ValidationException;21import com.consol.citrus.validation.json.JsonMessageValidator;22import com.consol.citrus.validation.json.JsonSchemaValidationContext;23import com.consol.citrus.validation.json.JsonValidationContext;24import com.consol.citrus.validation.json.JsonValidationProcessor;25import com.consol.citrus.validation.json.JsonValidationUtils;26import com.consol.citrus.validation.json.JsonSchemaValidationContext.SchemaValidationType;27import com.consol.citrus.validation.json.JsonValidationContext.ValidationType;28import com.consol.citrus.validation.json.report.GraciousProcessingReport;29import com.consol.citrus.validation.json.JsonMessageValidationContext;30import com.consol.citrus.message.Message;31import com.consol.citrus.message.MessageType;32import com.consol.citrus.message.MessageHeaders;33import com.consol.citrus.validation.DefaultValidationContext;34import com.consol.citrus.validation.DefaultValidationContextFactory;35import com.consol.citrus.validation.MessageValidator;36import com.consol.citrus.validation.ValidationContext;37import com.consol.citrus.validation.ValidationContextFactory;38import com.consol.citrus.validation.ValidationException;39import com.consol.citrus.validation.json.JsonMessage

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1import com.consol.citrus.dsl.runner.TestRunner;2import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;3import com.consol.citrus.validation.json.JsonTextMessageValidator;4import com.consol.citrus.validation.json.report.GraciousProcessingReport;5import org.testng.annotations.Test;6public class GraciousProcessingReportTest extends TestNGCitrusTestDesigner {7 public void test() {8 TestRunner runner = createTestRunner();9 runner.echo("Test to use GraciousProcessingReport class of com.consol.citrus.validation.json.report package");10 JsonTextMessageValidator jsonTextMessageValidator = new JsonTextMessageValidator();11 jsonTextMessageValidator.setReport(new GraciousProcessingReport());12 jsonTextMessageValidator.validateMessage(runner, "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}", "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}");13 }14}

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1package com.consol.citrus.validation.json.report;2import com.consol.citrus.exceptions.ValidationException;3import com.consol.citrus.validation.json.JsonMessageValidationContext;4import com.consol.citrus.validation.json.JsonPathMessageValidator;5import com.consol.citrus.validation.json.report.GraciousProcessingReport;6import com.consol.citrus.validation.matcher.ValidationMatcherUtils;7import com.consol.citrus.validation.script.GroovyJsonMessageValidator;8import com.consol.citrus.validation.xml.XmlMessageValidationContext;9import com.consol.citrus.xml.XsdSchemaRepository;10import com.consol.citrus.xml.namespace.NamespaceContextBuilder;11import com.consol.citrus.xml.schema.XsdSchema;12import com.consol.citrus.xml.schema.XsdSchemaValidationContext;13import com.consol.citrus.xml.schema.XsdSchemaValidationErrorHandler;14import com.consol.citrus.xml.schema.XsdSchemaValidator;15import com.fasterxml.jackson.databind.JsonNode;16import com.fasterxml.jackson.databind.ObjectMapper;17import com.fasterxml.jackson.databind.node.ObjectNode;18import com.jayway.jsonpath.DocumentContext;19import com.jayway.jsonpath.JsonPath;20import com.jayway.jsonpath.PathNotFoundException;21import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;22import com.jayway.jsonpath.spi.json.JacksonJsonObjectJsonProvider;23import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;24import net.minidev.json.JSONArray;25import net.minidev.json.JSONObject;26import net.minidev.json.parser.JSONParser;27import net.minidev.json.parser.ParseException;28import org.slf4j.Logger;29import org.slf4j.LoggerFactory;30import org.springframework.util.CollectionUtils;31import org.springframework.util.StringUtils;32import org.springframework.xml.validation.XmlValidator;33import org.xml.sax.SAXException;34import javax.xml.transform.Source;35import javax.xml.transform.dom.DOMSource;36import java.io.IOException;37import java.util.*;38public class GraciousJsonPathMessageValidator extends JsonPathMessageValidator {39 private static final Logger LOG = LoggerFactory.getLogger(GraciousJsonPathMessageValidator.class);40 private static final String JSON_PATH_VARIABLE_PREFIX = "$";41 private final ObjectMapper objectMapper = new ObjectMapper();42 private final GraciousProcessingReport processingReport = new GraciousProcessingReport();43 private final JsonPathMessageValidationContext validationContext = new JsonPathMessageValidationContext();44 private final XsdSchemaValidator schemaValidator = new XsdSchemaValidator();

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1package com.consol.citrus.validation.json.report;2import java.util.ArrayList;3import java.util.List;4import com.consol.citrus.validation.json.JsonMessageValidationContext;5import com.consol.citrus.validation.json.JsonPathMessageValidationContext;6import com.consol.citrus.validation.json.JsonPathMessageValidationContext.JsonPathValidationResult;7import com.consol.citrus.validation.json.JsonPathMessageValidationContext.JsonPathValidationResultType;8import com.consol.citrus.validation.json.JsonPathMessageValidationContext.JsonPathValidationResultType.JsonPathValidationResultTypeValue;9import com.consol.citrus.validation.json.JsonValidationResult;10import com.consol.citrus.validation.json.JsonValidationResult.JsonValidationResultType;11import com.consol.citrus.validation.json.JsonValidationResult.JsonValidationResultType.JsonValidationResultTypeValue;12import com.consol.citrus.validation.json.JsonValidationUtils;13import com.consol.citrus.validation.json.report.GraciousProcessingReport;14import com.consol.citrus.validation.json.report.JsonMessageValidationReport;15import com.consol.citrus.validation.json.report.JsonPathMessageValidationReport;16import com.consol.citrus.validation.report.ValidationReport;17import com.consol.citrus.validation.xml.XmlMessageValidationContext;18import com.consol.citrus.validation.xml.XmlMessageValidationContext.XmlValidationResult;19import com.consol.citrus.validation.xml.XmlMessageValidationContext.XmlValidationResultType;20import com.consol.citrus.validation.xml.XmlMessageValidationContext.XmlValidationResultType.XmlValidationResultTypeValue;21import com.consol.citrus.validation.xml.XmlValidationUtils;22import com.consol.citrus.validation.xml.report.XmlMessageValidationReport;23import com.fasterxml.jackson.databind.JsonNode;24import com.fasterxml.jackson.databind.node.ArrayNode;25import com.fasterxml.jackson.databind.node.ObjectNode;26import com.fasterxml.jackson.databind.node.TextNode;27import com.github.fge.jsonschema.core.exceptions.ProcessingException;28import com.github.fge.jsonschema.core.report.ProcessingMessage;29import com.github.fge.jsonschema.core.report.ProcessingReport;30import com.github.fge.jsonschema.core.util.AsJson;31import com.github.fge.jsonschema.core.util.JsonLoader;32import com.github.fge.jsonschema.core.util.ValueHolder;33import com.github.fge.jsonschema.core.util.ValueHolder.Simple;34import com.github.fge.jsonschema.processors.data.FullData;35import com.github.fge.msgsimple.bundle.MessageBundle;36import com.github.fge.msgsimple.load.MessageBundles;37import

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;2import com.consol.citrus.json.report.GraciousProcessingReport;3import org.testng.annotations.Test;4import java.io.IOException;5import java.net.URL;6import java.nio.file.Files;7import java.nio.file.Paths;8import java.util.List;9import java.util.stream.Collectors;10import java.util.stream.Stream;11public class 4 extends TestNGCitrusTestDesigner {12 public void MyTest() {13 variable("json", Files.readAllBytes(Paths.get("C:\\Users\\user\\Desktop\\json.json")));14 variable("schema", Files.readAllBytes(Paths.get("C:\\Users\\user\\Desktop\\schema.json")));15 GraciousProcessingReport report = new GraciousProcessingReport();16 report.setPrintValidationErrors(true);17 report.setPrintValidationWarnings(true);18 echo("Validating JSON payload: ${json}");19 echo("Against JSON schema: ${schema}");20 json().validate("${json}", "${schema}", report);21 echo("Validation report: ${report}");22 }23}24import com.consol.citrus.dsl.testng.TestNGCitrusTestDesigner;25import com.consol.citrus.json.report.GraciousProcessingReport;26import org.testng.annotations.Test;27import java.io.IOException;28import java.net.URL;29import java.nio.file.Files;30import java.nio.file.Paths;31import java.util.List;32import java.util.stream.Collectors;33import java.util.stream.Stream;34public class 5 extends TestNGCitrusTestDesigner {35 public void MyTest() {36 variable("json", Files.readAllBytes(Paths.get("C:\\Users\\user\\Desktop\\json.json")));37 variable("schema", Files.readAllBytes(Paths.get("C:\\Users\\user\\Desktop\\schema.json")));38 GraciousProcessingReport report = new GraciousProcessingReport();39 report.setPrintValidationErrors(true);40 report.setPrintValidationWarnings(true);41 echo("Validating JSON payload: ${json}");42 echo("Against JSON schema: ${schema}");43 json().validate("${json}", "${schema}", report);44 echo("Validation report: ${report}");45 }46}47import com

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1public class GraciousProcessingReport extends ProcessingReport {2 private final List<ProcessingMessage> messages = new ArrayList<ProcessingMessage>();3 private final LogLevel logLevel;4 private final boolean failOnErrors;5 private final boolean failOnWarnings;6 private final boolean failOnInfos;7 private final boolean failOnUnknown;8 private final boolean failOnMissing;9 public GraciousProcessingReport(LogLevel logLevel, boolean failOnErrors, boolean failOnWarnings, boolean failOnInfos, boolean failOnUnknown, boolean failOnMissing) {10 this.logLevel = logLevel;11 this.failOnErrors = failOnErrors;12 this.failOnWarnings = failOnWarnings;13 this.failOnInfos = failOnInfos;14 this.failOnUnknown = failOnUnknown;15 this.failOnMissing = failOnMissing;16 }17 public void log(LogLevel level, ProcessingMessage message) throws ProcessingException {18 if (logLevel.ordinal() >= level.ordinal()) {19 System.out.println("[" + level + "] " + message.getMessage());20 }21 messages.add(message);22 }23 public void flush() throws ProcessingException {24 for (ProcessingMessage message : messages) {25 if (failOnErrors && message.asJson().get("level").asText().equals(LogLevel.ERROR.toString())) {26 throw new ValidationException("Validation failed: " + message.getMessage());27 }28 if (failOnWarnings && message.asJson().get("level").asText().equals(LogLevel.WARNING.toString())) {29 throw new ValidationException("Validation failed: " + message.getMessage());30 }31 if (failOnInfos && message.asJson().get("level").asText().equals(LogLevel.INFO.toString())) {32 throw new ValidationException("Validation failed: " + message.getMessage());33 }34 if (failOnUnknown && message.asJson().get("level").asText().equals(LogLevel.UNKNOWN.toString())) {35 throw new ValidationException("Validation failed: " + message.getMessage());36 }37 if (failOnMissing && message.asJson().get("level").asText().equals(LogLevel.MISSING.toString())) {38 throw new ValidationException("Validation failed: " + message.getMessage());39 }40 }41 }42}43public class GraciousProcessingReport implements ProcessingReport {

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1import com.consol.citrus.validation.json.JsonTextMessageValidator;2import com.consol.citrus.validation.json.report.GraciousProcessingReport;3import com.consol.citrus.validation.json.report.JsonValidationReport;4import com.consol.citrus.validation.json.report.JsonValidationReportEntry;5import org.testng.annotations.Test;6import java.io.IOException;7import java.util.List;8public class JsonValidatorTest {9 public void testJsonValidator() throws IOException {10 JsonTextMessageValidator validator = new JsonTextMessageValidator();11 validator.setSchemaPath("classpath:expected.json");12 validator.setIgnoreUnexpectedElements(true);13 JsonValidationReport report = new JsonValidationReport();14 validator.validateMessagePayload(report, "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}");15 if (!report.isSuccess()) {16 GraciousProcessingReport processingReport = new GraciousProcessingReport();17 processingReport.mergeWith(report.getReport());18 List<JsonValidationReportEntry> reportEntries = processingReport.getReportEntries();19 for (JsonValidationReportEntry entry : reportEntries) {20 System.out.println(entry);21 }22 }23 }24}25JsonValidationReportEntry{path='$.city', message='Unexpected field found: city', type='UnexpectedField'}26JsonValidationReportEntry{path='$.age', message='Unexpected field found: age', type='UnexpectedField'}27JsonValidationReportEntry{path='$.name', message='Unexpected field found: name', type='UnexpectedField'}28import com.consol.citrus.validation.json.JsonTextMessageValidator;29import com.consol.citrus.validation.json.report.GraciousProcessingReport;30import com.consol.citrus.validation.json.report.JsonValidationReport;31import com.consol.citrus.validation.json.report.JsonValidationReportEntry;32import org.testng.annotations.Test;33import java.io.IOException;34import java.util.List;35public class JsonValidatorTest {36 public void testJsonValidator() throws IOException {

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1public class 4.java {2public static void main(String[] args) {3try {4String schema = "{ \"type\" : \"object\", \"properties\" : { \"id\" : { \"type\" : \"string\" }, \"name\" : { \"type\" : \"string\" }, \"age\" : { \"type\" : \"number\" }, \"address\" : { \"type\" : \"string\" } }, \"required\" : [ \"id\", \"name\", \"age\", \"address\" ] }";5String payload = "{ \"id\" : \"123\", \"name\" : \"John\", \"age\" : 30, \"address\" : \"New York\" }";6JsonMessageProcessor jsonProcessor = new JsonMessageProcessor();7JsonSchemaValidator validator = new JsonSchemaValidator();8GraciousProcessingReport report = new GraciousProcessingReport();9validator.setSchema(schema);10jsonProcessor.setSchemaValidator(validator);11jsonProcessor.setReport(report);12jsonProcessor.validateMessage(MessageType.JSON.name(), payload, null);13System.out.println(report.toString());14} catch (Exception e) {15e.printStackTrace();16}17}18}19{20 "schema" : {21 },22 "instance" : {23 }24}

Full Screen

Full Screen

GraciousProcessingReport

Using AI Code Generation

copy

Full Screen

1public void test_4() throws Exception {2 send(sendMessageBuilder -> sendMessageBuilder.endpoint(httpServer)3 .messageType(MessageType.PLAINTEXT)4 );5 receive(receiveMessageBuilder -> receiveMessageBuilder.endpoint(httpServer)6 .messageType(MessageType.PLAINTEXT)7 );8 validate(jsonMessageValidatorBuilder -> jsonMessageValidatorBuilder.report(new GraciousProcessingReport())9 .ignore("user.firstname")10 .ignore("user.lastname")11 .jsonPath("$.user.firstname", "John")12 .jsonPath("$.user.lastname", "Doe")13 );14}15public void test_5() throws Exception {

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Citrus automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Test Your Web Or Mobile Apps On 3000+ Browsers

Signup for free

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful