package org.fakekoji.api.http.rest;

import java.util.List;
import java.util.Map;
import java.util.Optional;

class RestUtils {

    static Optional<String> extractParamValue(Map<String, List<String>> paramsMap, String param) {
        return Optional.ofNullable(paramsMap.get(param))
                .filter(list -> list.size() == 1)
                .map(list -> list.get(0));
    }

}

@@ -30,16 +30,165 @@
 .rest;%0A%0A
+import org.fakekoji.functional.Result;%0Aimport org.fakekoji.functional.Tuple;%0Aimport org.fakekoji.jobmanager.model.Product;%0A%0Aimport java.util.Arrays;%0A
 import j
@@ -527,10 +527,1852 @@
 %0A    %7D%0A%0A
+    static Result%3CString, OToolError%3E extractMandatoryParamValue(Map%3CString, List%3CString%3E%3E paramsMap, String param) %7B%0A        return extractParamValue(paramsMap, param).%3CResult%3CString, OToolError%3E%3Emap(Result::ok)%0A                .orElseGet(() -%3E Result.err(new OToolError(%0A                        %22Missing mandatory parameter: '%22 + param + %22'!%22,%0A                        400%0A                )));%0A    %7D%0A%0A    static Result%3CTuple%3CProduct, Product%3E, OToolError%3E extractProducts(final Map%3CString, List%3CString%3E%3E paramsMap) %7B%0A        return extractMandatoryParamValue(paramsMap, %22from%22).flatMap(fromValue -%3E%0A           extractProduct(fromValue).flatMap(fromProduct -%3E%0A               extractMandatoryParamValue(paramsMap, %22to%22).flatMap(toValue -%3E%0A                   extractProduct(toValue).flatMap(toProduct -%3E%0A                       Result.ok(new Tuple%3C%3E(fromProduct, toProduct))%0A                   )%0A               )%0A           )%0A        );%0A    %7D%0A%0A    static Result%3CProduct, OToolError%3E extractProduct(final String paramValue) %7B%0A        final String%5B%5D split = paramValue.split(%22,%22);%0A        if (split.length != 2 %7C%7C split%5B0%5D.trim().isEmpty() %7C%7C split%5B1%5D.trim().isEmpty()) %7B%0A            return Result.err(new OToolError(%22Expected format jdkVersionId,packageName%22, 400));%0A        %7D%0A        return Result.ok(new Product(split%5B0%5D, split%5B1%5D));%0A    %7D%0A%0A    static Result%3CList%3CString%3E, OToolError%3E extractProjectIds(Map%3CString, List%3CString%3E%3E paramsMap) %7B%0A        final Optional%3CString%3E projectIdsOptional = extractParamValue(paramsMap, %22projects%22);%0A        return projectIdsOptional.%3CResult%3CList%3CString%3E, OToolError%3E%3Emap(s -%3E%0A                Result.ok(Arrays.asList(s.split(%22,%22)))%0A        ).orElseGet(() -%3E Result.err(new OToolError(%0A                %22projects are mandatory. Use get/projects?as=list to get them all%22,%0A                400%0A        )));%0A    %7D%0A
 %7D%0A

Create helper methods for product bump