All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.perfana.event.PerfanaEvent Maven / Gradle / Ivy

/*
 *    Copyright 2020-2023  Peter Paul Bakker @ perfana.io, Daniel Moll @ perfana.io
 *
 *    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 io.perfana.event;

import io.perfana.client.PerfanaClient;
import io.perfana.client.PerfanaUtils;
import io.perfana.client.api.PerfanaTestContext;
import io.perfana.client.domain.ConfigItem;
import io.perfana.client.domain.TestRunConfigJson;
import io.perfana.client.domain.TestRunConfigKeyValue;
import io.perfana.client.domain.TestRunConfigKeys;
import io.perfana.client.exception.PerfanaAssertResultsException;
import io.perfana.client.exception.PerfanaAssertionsAreFalse;
import io.perfana.client.exception.PerfanaClientException;
import io.perfana.eventscheduler.api.*;
import io.perfana.eventscheduler.api.config.TestContext;
import io.perfana.eventscheduler.api.message.EventMessage;
import io.perfana.eventscheduler.api.message.EventMessageBus;
import io.perfana.eventscheduler.api.message.EventMessageReceiver;
import io.perfana.eventscheduler.exception.handler.KillSwitchException;
import io.perfana.eventscheduler.util.TestRunConfigUtil;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class PerfanaEvent extends EventAdapter {

    private static final String CLASSNAME = PerfanaEvent.class.getName();
    public static final String PLUGIN_NAME = "perfana-java-client";
    private final String eventName;

    private final PerfanaTestContext perfanaTestContext;

    private final EventMessageBus messageBus;

    private final Map receivedVariables = new ConcurrentHashMap<>();

    private final PerfanaClient perfanaClient;

    private String abortDetailMessage = null;
    // save some state to do the status check
    private EventCheck eventCheck;

    PerfanaEvent(PerfanaEventContext context, TestContext testContext,EventMessageBus messageBus, EventLogger logger) {
        super(context, testContext, messageBus, logger);
        this.eventCheck = new EventCheck(context.getName(), CLASSNAME, EventStatus.UNKNOWN, "No known result yet. Try again some time later.");
        this.eventName = context.getName();
        this.perfanaTestContext = PerfanaUtils.createPerfanaTestContext(context, testContext);
        this.messageBus = messageBus;

        this.perfanaClient = PerfanaUtils.createPerfanaClient(context, perfanaTestContext, logger);

        EventMessageReceiver eventMessageReceiver = message -> {
            // a test-run-config message
            if (message.getVariables().getOrDefault("message-type", "").equals("test-run-config")) {
                logger.debug("received test-run-config message from " + message.getPluginName());
                addTestRunConfig(message);
            }
            else if (!message.getVariables().isEmpty()) {
                logger.debug("received variables from " + message.getPluginName() + ": " + message.getVariables());
                receivedVariables.putAll(message.getVariables());
            }
        };
        this.messageBus.addReceiver(eventMessageReceiver);
    }

    private void addTestRunConfig(EventMessage message) {

        Map variables = message.getVariables();

        String output = variables.get("output");
        String tags = variables.getOrDefault("tags", "");

        switch (output) {
            case "key":

                TestRunConfigKeyValue.TestRunConfigKeyValueBuilder testRunConfig = TestRunConfigKeyValue.builder()
                        .testRunId(perfanaTestContext.getTestRunId())
                        .application(perfanaTestContext.getSystemUnderTest())
                        .testEnvironment(perfanaTestContext.getTestEnvironment())
                        .testType(perfanaTestContext.getWorkload())
                        .key(variables.get("key"))
                        .value(replaceNullWithEmptyString(message.getMessage()));

                Arrays.stream(tags.split(",")).forEach(testRunConfig::tag);

                perfanaClient.addTestRunConfigKeyValue(testRunConfig.build());

                break;
            case "json":

                TestRunConfigJson.TestRunConfigJsonBuilder testRunConfigJson = TestRunConfigJson.builder()
                        .testRunId(perfanaTestContext.getTestRunId())
                        .application(perfanaTestContext.getSystemUnderTest())
                        .testEnvironment(perfanaTestContext.getTestEnvironment())
                        .testType(perfanaTestContext.getWorkload())
                        .json(message.getMessage());

                String excludes = variables.getOrDefault("excludes", "");
                String includes = variables.getOrDefault("includes", "");

                Arrays.stream(excludes.split(",")).forEach(testRunConfigJson::excludeItem);
                Arrays.stream(includes.split(",")).forEach(testRunConfigJson::includeItem);

                Arrays.stream(tags.split(",")).forEach(testRunConfigJson::tag);

                perfanaClient.addTestRunConfigJson(testRunConfigJson.build());
                break;
            case "keys":
                TestRunConfigKeys.TestRunConfigKeysBuilder keysBuilder = TestRunConfigKeys.builder()
                        .testRunId(perfanaTestContext.getTestRunId())
                        .application(perfanaTestContext.getSystemUnderTest())
                        .testEnvironment(perfanaTestContext.getTestEnvironment())
                        .testType(perfanaTestContext.getWorkload());

                Arrays.stream(tags.split(",")).forEach(keysBuilder::tag);

                String keyValuePairsString = message.getMessage();

                // -1 to keep empty strings in split
                List keyValuePairs = Arrays.asList(keyValuePairsString.split(TestRunConfigUtil.MESSAGE_KEY_VALUE_DELIMITER, -1));

                if (keyValuePairs.size() % 2 != 0) {
                    logger.error("skip send of test config key value pairs: received string with uneven number of key-value items: " + keyValuePairs.size());
                } else {
                    for (int i = 0; i < keyValuePairs.size(); i = i + 2) {
                        keysBuilder.configItem(new ConfigItem(keyValuePairs.get(i), keyValuePairs.get(i + 1)));
                    }
                    perfanaClient.addTestRunConfigKeys(keysBuilder.build());
                }
                break;
            default:
                logger.error("received test-run-config message with unexpected output type: " + output);
                break;
        }
    }

    private String replaceNullWithEmptyString(String text) {
        return text == null ? "" : text;
    }

    @Override
    public void beforeTest() {
        sendTestRunConfig();
    }

    private void sendTestRunConfig() {
        Map configKeyValues = createTestRunConfigKeyValues();
        EventMessage message = TestRunConfigUtil.createTestRunConfigMessageKeys(eventContext.getName(), configKeyValues, PLUGIN_NAME);
        this.eventMessageBus.send(message);
    }

    private Map createTestRunConfigKeyValues() {
        String prefix = "event." + eventContext.getName() + ".";
        Map lines = new HashMap<>();
        lines.put(prefix + "perfanaUrl", eventContext.getPerfanaUrl());
        lines.put(prefix + "isAssertResultsEnabled", String.valueOf(eventContext.isAssertResultsEnabled()));
        lines.put(prefix + "scheduleScript", eventContext.getScheduleScript());
        lines.put(prefix + "variables", String.valueOf(eventContext.getVariables()));
        return lines;
    }

    @Override
    public void startTest() {
        perfanaClient.callPerfanaEvent(perfanaTestContext, "Test start", "Test run started");
    }

    @Override
    public void afterTest() {

        if (abortDetailMessage != null) {
            perfanaClient.callPerfanaEvent(perfanaTestContext, "Test abort", abortDetailMessage);
        }
        else {
            perfanaClient.callPerfanaEvent(perfanaTestContext, "Test end", "Test run completed");
        }

        finalizePerfanaTestRun();
    }

    /**
     * Calls out to Perfana with completed = true. Also checks the assertions of the test run.
     */
    private void finalizePerfanaTestRun() {
        perfanaClient.callPerfanaTestEndpoint(perfanaTestContext, true, receivedVariables);

        // assume all is ok, will be overridden in case of assertResult exceptions
        eventCheck = new EventCheck(eventName, CLASSNAME, EventStatus.SUCCESS, "All ok!");
        try {
            String text = perfanaClient.assertResults();
            logger.info("Received Perfana check results: " + text);
        } catch (PerfanaAssertResultsException e) {
            logger.error("Perfana check results failed: " + e.getMessage());
            eventCheck = new EventCheck(eventName, CLASSNAME, EventStatus.FAILURE, "Perfana check results failed: " + e.getMessage());
        } catch (PerfanaClientException e) {
            logger.error("Perfana check results failed.", e);
            eventCheck = new EventCheck(eventName, CLASSNAME, EventStatus.FAILURE, "Failed to get check results: " + e.getMessage());
        } catch (PerfanaAssertionsAreFalse perfanaAssertionsAreFalse) {
            eventCheck = new EventCheck(eventName, CLASSNAME, EventStatus.FAILURE, perfanaAssertionsAreFalse.getMessage());
        }
    }

    @Override
    public void abortTest() {
        String eventTitle = "Test aborted";
        String eventDescription = abortDetailMessage == null ? "manually aborted" : abortDetailMessage;
        perfanaClient.callPerfanaEvent(perfanaTestContext, eventTitle, eventDescription);

        this.eventCheck = new EventCheck(eventName, CLASSNAME, EventStatus.ABORTED, eventDescription);

        // maybe only when not manually aborted? e.g. abortDetailMessage is set?
        finalizePerfanaTestRun();
    }

    @Override
    public EventCheck check() {
        return eventCheck;
    }

    @Override
    public void keepAlive() {
        logger.debug("Keep alive called");
        try {
            perfanaClient.callPerfanaTestEndpoint(perfanaTestContext, false, receivedVariables);
        } catch (KillSwitchException killSwitchException) {
            abortDetailMessage = killSwitchException.getMessage();
            throw killSwitchException;
        }
    }

    @Override
    public void customEvent(CustomEvent customEvent) {
        try {
            perfanaClient.callPerfanaEvent(perfanaTestContext, customEvent.getName(), customEvent.getDescription());
        } catch (Exception e) {
            logger.error("Perfana call event failed", e);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy