JCMA problem with verndor app check

The origin of the problem
In January this year a new feature was released - App Vendor Check. It looks very promising - thanks to this I want to validate all the data of my application and display any instructions on what to do to improve the quality of the data.

I already have a working migration and in case of sending correct data everything work smooth.

The main problem
is that my implementation being almost the same as in the example, however the vendor checks don’t run/apear at all. From the UI side, it looks like I didn’t implement them. The same in log file.

We have spring 2 in our app so i used @Component against Component in xml file - Coudl it be a problem?
Or any other idea why my code does not connect with JCMA?

Hello @JacekOstrowski :wave:

Exposing via @Component should be fine. But if you’re not explicitly exposing it as com.atlassian.migration.app.check.PreMigrationCheckRepository you should check if that interface is directly implemented by the class being annotated.

Another thing you could check is in UPM’s “Manage add-ons” screen if the modules are being correctly enabled, like in the screenshot below:

Some extra troubleshooting I can think of:

  • Ensure your have installed JCMA 1.7.7 or later with the dark feature com.atlassian.jira.migration-assistant.enable.app-vendor-check enabled
  • Install our sample app and check whether those checks would work for you. (just to validate whether the problem is on your side or not)

Please let us know if that was helpful.

Andre

Hello @AndreRosot

Thank you for your answer. The one of problems was that I haven’t been familiar to dark features flags and didn’t realise that i have to turn flag com.atlassian.jira.migration-assistant.enable.app-vendor-check on. Just done it:

After setting the flag, I see another option in checks (so I assumed everything works):

However i was not able to trigger anny warning/error message due to my check. It seems my validation checks are still not triggered at all. I always got an info about valid response from check (as on above screen).

So I cloned example app repo and run it just to check how it worl/should look but the app seems to be not avaliable in cloud, so JCMA even didn’t catch the app to migrate.

At this point I don’t know what to do to be able to display an check message.

My check repo class

import com.atlassian.migration.app.check.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.*;

import static com.atlassian.migration.app.check.CheckResultBuilder.resultBuilder;
import static com.atlassian.migration.app.check.CheckStatus.*;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonMap;

@Component
@Slf4j
public class ExampleMigrationChecks implements PreMigrationCheckRepository {

    @Override
    public Set<CheckSpec> getAvailableChecks() {

        HashSet<CheckSpec> checks = new HashSet<>();

        try {
            // You can have multiple checks, up to 3 checks will be executed. If there's more than that, some checks will be ignored.
            checks.add(createTspValidationCheck());
            checks.add(createTSPDeferencesCheck());
        } catch (IllegalArgumentException e) {
            // Invalid checkIds, title and stepsToResolve can throw this exception from CheckSpec's constructor
            e.printStackTrace();
        }

        return checks;
    }

    /*
     * Perform the checks for that specific checkId.
     * If you have multiple checks they may be executed in parallel.
     * This method should reply under 5 minutes to avoid time-outs.
     */
    @Override
    public CheckResult executeCheck(String checkId, MigrationPlanContext migrationPlanContext) {
        log.info("Running checks for plan {}", migrationPlanContext.getPlanName());
        log.info("Context of the migration: {}", Arrays.toString(migrationPlanContext.getContainers().toArray()));

        switch (checkId) {
            case "Test-validation-check":
                return new CheckResult(SUCCESS); // You probably want to run some checks. Here we just keep it simple
            case "Test-deference-check":
                return executeComplicatedCheck();
            default:
                throw new IllegalStateException("Unknown check: " + checkId);
        }
    }

    private static CheckSpec createTestValidationCheck() {
        // Creating our first check. CheckSpec will validate some base pre-conditions, like valid checkId.
        CheckSpec simplePermissionCheck = new CheckSpec(
                "Test-validation-check",                              // Unique ID across all the implemented checks
                "Test  Validation Check",                            // Check's title
                "There are invalid scheduled issues!",                                            // A brief description
                singletonMap("template1", "Steps how to resolve this check") // A template that contains instructions on how to resolve this check
        );

        /* Before returning these checks we advise to invoke the `validate()` method. We truncate the content
         * based on the following rules:
         * `title` should not exceed 60 characters
         * `stepsToResolve` should not exceed 1050 characters
         *
         * validate() will return List<String> that contains these error messages
         */
        List<String> validationErrors = simplePermissionCheck.validate();
        if (!validationErrors.isEmpty()) {
            log.warn("Validation errors found: " + validationErrors);
        }

        return simplePermissionCheck;
    }

    private CheckSpec createTestDeferencesCheck() {
        CheckSpec complicatedCheckWithTemplates = new CheckSpec(
                "Test-deference-check",
                "Test  Deference check",
                "Some configuration is impossible to migrate",
                buildStepsToResolveTemplates());
        List<String> validationErrors = complicatedCheckWithTemplates.validate();
        if (!validationErrors.isEmpty()) {
            log.warn("Validation errors found: " + validationErrors);
        }
        return complicatedCheckWithTemplates;
    }

    private CheckResult executeComplicatedCheck() {
        try {
            // Optional details that can be downloaded by the user in CSV format
            CsvFileContent csvFile = new CsvFileContent(asList("Jira Issue", "Description"));
            csvFile.addRow(asList("FTP-13", "A very important customer request"));
            csvFile.addRow(asList("FTP-9", "Another very important customer request"));

            // Using CheckResultBuilder, but you can also directly instantiate CheckResult
            CheckResultBuilder builder = resultBuilder(BLOCKING).withCsvFileContent(csvFile);

            if (checkSomething()) { // Let's suppose that you detected issues with permissions, for example
                builder.withStepsToResolveKey("Test-steps-to-resolve-missing-team-field"); // Be sure to use an existing stepsToResolveKey previously provided on the CheckSpec
            }

            return builder.build();
        } catch (
                Exception e) {  // Optional. If your code throws an exception, we'll implicitly convert it into a failed result like this one.
            return resultBuilder(CHECK_EXECUTION_ERROR).build();
        }
    }

    private static boolean checkSomething() {
        return true; // We aren't really checking :)
    }

    /*
     * stepsToResolve is a map between a stepsToResolveKey and the actual description of steps to unblock the customer.
     * The stepsToResolveKey has to match the value used on CheckResult
     */
    private Map<String, String> buildStepsToResolveTemplates() {
        /*
         * You can format the instructions for better readability, this will be reflected in the UI.
         * Only these 3 tags are recognized:
         * <paragraph>
         * <ordered-item>
         * <unordered-item>
         *
         * We encourage all texts to be placed in proper tags and within limit of 1050 (including tags) characters,
         * any invalid/improper tag is not guaranteed to be rendered properly in UI. Templates exceeding the limit will
         * be truncated.
         *
         * These templates allow you to choose the appropriate instructions to resolve the issue when returning a
         * `CheckResult`.
         */
        Map<String, String> stepsToResolve = new HashMap<>();
        stepsToResolve.put("Test-steps-to-resolve-missing-team-field",
                "<paragraph>Some projects had issue where field \"Team\" was not set. Please follow steps to fix those issues.</> " +
                        "<unordered-item>Go to project mentioned in the csv file.</>" +
                        "<unordered-item>Open the respective issues mentioned in the csv file</>" +
                        "<unordered-item>Update the value of field:Team</>");

        // In this example we won't use it, but you can have multiple stepsToResolve entries
        stepsToResolve.put("Test-steps-to-resolve-permission-error",
                "<paragraph>App do not have right permissions at some projects. Please follow steps to fix those issues.</>" +
                        "<ordered-item>Go to project settings for project keys mentioned in the csv file.</>" +
                        "<ordered-item>Assign read/write permissions to app</>" +
                        "<ordered-item>Re-run the check</>");

        return stepsToResolve;
    }
}

Hi @JacekOstrowski !

Your code looks correct. There’s one part I really can’t inspect (the implementation of createTspValidationCheck and createTSPDeferencesCheck) but I’ll assume that’s fine.

I’d like to refer to my previous message and ask you to inspect whether your class ExampleMigrationChecks is being exported as an OSGi module.

That would be a likely cause for your checks being ignored.

Regards,

Andre