public APIGatewayProxyResponseEvent updateAppConfig()

in services/settings-service/src/main/java/com/amazon/aws/partners/saasfactory/saasboost/SettingsService.java [602:724]


    public APIGatewayProxyResponseEvent updateAppConfig(Map<String, Object> event, Context context) {
        if (Utils.isBlank(SAAS_BOOST_EVENT_BUS)) {
            throw new IllegalStateException("Missing environment variable SAAS_BOOST_EVENT_BUS");
        }
        if (Utils.isBlank(API_GATEWAY_HOST)) {
            throw new IllegalStateException("Missing required environment variable API_GATEWAY_HOST");
        }
        if (Utils.isBlank(API_GATEWAY_STAGE)) {
            throw new IllegalStateException("Missing required environment variable API_GATEWAY_STAGE");
        }
        if (Utils.isBlank(API_TRUST_ROLE)) {
            throw new IllegalStateException("Missing required environment variable API_TRUST_ROLE");
        }
        if (Utils.warmup(event)) {
            //LOGGER.info("Warming up");
            return new APIGatewayProxyResponseEvent().withHeaders(CORS).withStatusCode(200);
        }

        final long startTimeMillis = System.currentTimeMillis();
        LOGGER.info("SettingsService::updateAppConfig");
        Utils.logRequestEvent(event);
        APIGatewayProxyResponseEvent response = null;

        try {
            AppConfig updatedAppConfig = Utils.fromJson((String) event.get("body"), AppConfig.class);
            if (updatedAppConfig == null) {
                response = new APIGatewayProxyResponseEvent()
                        .withHeaders(CORS)
                        .withStatusCode(400)
                        .withBody("{\"message\":\"Empty request body.\"}");
            } else if (updatedAppConfig.getName() == null || updatedAppConfig.getName().isEmpty()) {
                LOGGER.error("Can't update application configuration without an app name");
                response = new APIGatewayProxyResponseEvent()
                        .withHeaders(CORS)
                        .withStatusCode(400)
                        .withBody("{\"message\":\"Application name is required.\"");
            } else {
                AppConfig currentAppConfig = dal.getAppConfig();
                updatedAppConfig = dal.setAppConfig(updatedAppConfig);

                if (AppConfigHelper.isDomainChanged(currentAppConfig, updatedAppConfig)) {
                    LOGGER.info("AppConfig domain name has changed");
                    triggerDomainNameChange();
                }

                if (AppConfigHelper.isBillingChanged(currentAppConfig, updatedAppConfig)) {
                    String apiKey1 = currentAppConfig.getBilling() != null ? currentAppConfig.getBilling().getApiKey() : null;
                    String apiKey2 = updatedAppConfig.getBilling() != null ? updatedAppConfig.getBilling().getApiKey() : null;
                    LOGGER.info("AppConfig billing provider has changed {} != {}", apiKey1, apiKey2);
                    if (AppConfigHelper.isBillingFirstTime(currentAppConfig, updatedAppConfig)) {
                        // 1. We didn't have a billing provider and now we do, trigger setup
                        // Existing provisioned tenants won't be subscribed to a billing plan
                        // so we don't need to update the tenant stacks.
                        LOGGER.info("AppConfig now has a billing provider. Triggering billing setup.");
                        triggerBillingSetup();
                    } else if (AppConfigHelper.isBillingRemoved(currentAppConfig, updatedAppConfig)) {
                        // 2. We had a billing provider and now we don't, disable integration
                        LOGGER.info("AppConfig has removed the billing provider.");
                        // TODO how do we cleanup the billing provider integration?
                    } else {
                        // 3. We had a billing provider and we're just changing the value of the key, that is
                        // taken care of by dal.setAppConfig and we don't need to trigger a setup because
                        // it's already been done.
                        LOGGER.info("AppConfig billing provider API key in-place change.");
                    }
                }

                if (AppConfigHelper.isComputeChanged(currentAppConfig, updatedAppConfig) ||
                        AppConfigHelper.isAutoScalingChanged(currentAppConfig, updatedAppConfig)) {
                    LOGGER.info("AppConfig compute and/or scaling has changed. Triggering update of default setting tenants.");
                    // Get all the provisioned tenants who have not customized
                    // their compute settings so we can update them to the new
                    // global settings.
                    ApiRequest getTenantsRequest = ApiRequest.builder()
                            .resource("tenants/provisioned?overrideDefaults=false")
                            .method("GET")
                            .build();
                    SdkHttpFullRequest getTenantsApiRequest = ApiGatewayHelper.getApiRequest(API_GATEWAY_HOST, API_GATEWAY_STAGE, getTenantsRequest);
                    try {
                        String getTenantsResponseBody = ApiGatewayHelper.signAndExecuteApiRequest(getTenantsApiRequest, API_TRUST_ROLE, context.getAwsRequestId());
                        ArrayList<Map<String, Object>> provisionedTenantsWithDefaultSettings = Utils.fromJson(getTenantsResponseBody, ArrayList.class);
                        if (provisionedTenantsWithDefaultSettings != null) {
                            LOGGER.info("{} tenants with default settings to update", provisionedTenantsWithDefaultSettings.size());
                            for (Map<String, Object> tenant : provisionedTenantsWithDefaultSettings) {
                                // The onboarding service update tenant call expects to be given the
                                // values to use as parameters for the CloudFormation stack.
                                // AppConfig will delegate to ComputeSize for memory and cpu if it's set.
                                tenant.put("memory", updatedAppConfig.getDefaultMemory());
                                tenant.put("cpu", updatedAppConfig.getDefaultCpu());
                                tenant.put("minCount", updatedAppConfig.getMinCount());
                                tenant.put("maxCount", updatedAppConfig.getMaxCount());

                                LOGGER.info("Triggering update for tenant {}", tenant.get("id"));
                                Map<String, Object> systemApiRequest = new HashMap<>();
                                systemApiRequest.put("resource", "onboarding/update/tenant");
                                systemApiRequest.put("method", "PUT");
                                systemApiRequest.put("body", Utils.toJson(tenant));
                                publishEvent(SYSTEM_API_CALL_DETAIL_TYPE, SYSTEM_API_CALL_SOURCE, systemApiRequest);
                            }
                        }
                    } catch (Exception e) {
                        LOGGER.error("Error invoking API " + API_GATEWAY_STAGE + "/tenants/provisioned?overrideDefaults=false");
                        LOGGER.error(Utils.getFullStackTrace(e));
                        throw new RuntimeException(e);
                    }
                }

                response = new APIGatewayProxyResponseEvent()
                        .withStatusCode(200)
                        .withHeaders(CORS)
                        .withBody(Utils.toJson(updatedAppConfig));
            }
        } catch (Exception e) {
            LOGGER.error("Unable to update");
            response = new APIGatewayProxyResponseEvent()
                    .withHeaders(CORS)
                    .withStatusCode(400)
                    .withBody("{\"message\":\"Invalid JSON\"}");
        }
        long totalTimeMillis = System.currentTimeMillis() - startTimeMillis;
        LOGGER.info("SettingsService::updateAppConfig exec " + totalTimeMillis);
        return response;
    }