/* * QQQ - Low-code Application Framework for Engineers. * Copyright (C) 2021-2025. Kingsrook, LLC * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States * contact@kingsrook.com * https://github.com/Kingsrook/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.kingsrook.qbits.webhooks.processes; import java.time.Instant; import java.util.ArrayList; import java.util.List; import com.kingsrook.qbits.webhooks.actions.WebhookEventSender; import com.kingsrook.qbits.webhooks.model.Webhook; import com.kingsrook.qbits.webhooks.model.WebhookEvent; import com.kingsrook.qbits.webhooks.model.WebhookEventContent; import com.kingsrook.qbits.webhooks.model.WebhookEventSendLog; import com.kingsrook.qbits.webhooks.model.WebhookHealthStatus; import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine; import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLineInterface; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput; import com.kingsrook.qqq.backend.core.model.actions.processes.Status; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractLoadStep; import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ProcessSummaryProviderInterface; import com.kingsrook.qqq.backend.core.processes.implementations.general.ProcessSummaryWarningsAndErrorsRollup; import org.apache.commons.lang3.BooleanUtils; /******************************************************************************* ** sends test events to webhooks. *******************************************************************************/ public class SendTestEventToWebhookLoadStep extends AbstractLoadStep implements ProcessSummaryProviderInterface { private static final QLogger LOG = QLogger.getLogger(SendTestEventToWebhookLoadStep.class); private ProcessSummaryLine okLine = new ProcessSummaryLine(Status.OK) .withMessageSuffix(" delivered successfully") .withSingularPastMessage("was") .withPluralPastMessage("were"); private ProcessSummaryLine updatedToProbationLine = new ProcessSummaryLine(Status.INFO) .withMessageSuffix(" updated from Unhealthy to Probation") .withSingularPastMessage("was") .withPluralPastMessage("were"); private ProcessSummaryWarningsAndErrorsRollup errorsRollup = new ProcessSummaryWarningsAndErrorsRollup() .withDoReplaceSingletonCountLinesWithSuffixOnly(false) .withErrorTemplate(new ProcessSummaryLine(Status.ERROR) .withSingularPastMessage("failed to send with error message: ") .withPluralPastMessage("failed to send with error message: ")) .withOtherErrorsSummary(new ProcessSummaryLine(Status.ERROR) .withSingularPastMessage("had an other error.") .withPluralPastMessage("had other errors.")); /*************************************************************************** ** ***************************************************************************/ @Override public ArrayList getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen) { ArrayList rs = new ArrayList<>(); okLine.addSelfToListIfAnyCount(rs); updatedToProbationLine.addSelfToListIfAnyCount(rs); errorsRollup.addToList(rs); return rs; } /******************************************************************************* ** *******************************************************************************/ @Override public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException { boolean uponSuccessUpdateUnhealthyWebhooksToProbation = BooleanUtils.isTrue(runBackendStepInput.getValueBoolean("uponSuccessUpdateUnhealthyWebhooksToProbation")); List webhooksToUpdate = new ArrayList<>(); for(Webhook webhook : runBackendStepInput.getRecordsAsEntities(Webhook.class)) { WebhookEventSendLog sendLog; try { WebhookEvent event = new WebhookEvent().withContent(List.of(new WebhookEventContent() .withPostBody(""" { "webhookEventDetails": { "webhookEventTypeName": "testEvent", "eventTimestamp": "%s", "tableName": null, "apiName": null, "apiVersion": null }, "record": { } }""".formatted(Instant.now().toString())) )); sendLog = getWebhookEventSender().post(event, webhook); } catch(Exception e) { sendLog = new WebhookEventSendLog() .withSuccessful(false) .withErrorMessage(e.getMessage()); } if(sendLog.getSuccessful()) { if(WebhookHealthStatus.UNHEALTHY.getId().equals(webhook.getHealthStatusId())) { webhooksToUpdate.add(new QRecord().withValue("id", webhook.getId()).withValue("healthStatusId", WebhookHealthStatus.PROBATION.getId())); updatedToProbationLine.incrementCountAndAddPrimaryKey(webhook.getId()); } okLine.incrementCountAndAddPrimaryKey(webhook.getId()); } else { if(sendLog.getHttpStatusCode() != null) { errorsRollup.addError("Status Code: " + sendLog.getHttpStatusCode() + "; " + sendLog.getErrorMessage(), webhook.getId()); } else { errorsRollup.addError(sendLog.getErrorMessage(), webhook.getId()); } } } if(uponSuccessUpdateUnhealthyWebhooksToProbation && !webhooksToUpdate.isEmpty()) { new UpdateAction().execute(new UpdateInput(Webhook.TABLE_NAME).withRecords(webhooksToUpdate)); } } /*************************************************************************** ** ***************************************************************************/ protected WebhookEventSender getWebhookEventSender() { return new WebhookEventSender(); } }