diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogHeader.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogHeader.java new file mode 100644 index 00000000..53e6cd36 --- /dev/null +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogHeader.java @@ -0,0 +1,316 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.qqq.backend.module.api.model; + + +import java.time.Instant; +import java.util.List; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.data.QAssociation; +import com.kingsrook.qqq.backend.core.model.data.QField; +import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; + + +/******************************************************************************* + ** Entity bean for OutboundApiLog table + *******************************************************************************/ +public class OutboundAPILogHeader extends QRecordEntity +{ + public static final String TABLE_NAME = "outboundApiLogHeader"; + + @QField(isEditable = false) + private Integer id; + + @QField() + private Instant timestamp; + + @QField(possibleValueSourceName = "outboundApiMethod") + private String method; + + @QField(possibleValueSourceName = "outboundApiStatusCode") + private Integer statusCode; + + @QField(label = "URL") + private String url; + + @QAssociation(name = OutboundAPILogRequest.TABLE_NAME) + private List outboundAPILogRequestList; + + @QAssociation(name = OutboundAPILogResponse.TABLE_NAME) + private List outboundAPILogResponseList; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public OutboundAPILogHeader() + { + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public OutboundAPILogHeader(QRecord qRecord) throws QException + { + populateFromQRecord(qRecord); + } + + + + /******************************************************************************* + ** Getter for id + ** + *******************************************************************************/ + public Integer getId() + { + return id; + } + + + + /******************************************************************************* + ** Setter for id + ** + *******************************************************************************/ + public void setId(Integer id) + { + this.id = id; + } + + + + /******************************************************************************* + ** Fluent setter for id + ** + *******************************************************************************/ + public OutboundAPILogHeader withId(Integer id) + { + this.id = id; + return (this); + } + + + + /******************************************************************************* + ** Getter for timestamp + ** + *******************************************************************************/ + public Instant getTimestamp() + { + return timestamp; + } + + + + /******************************************************************************* + ** Setter for timestamp + ** + *******************************************************************************/ + public void setTimestamp(Instant timestamp) + { + this.timestamp = timestamp; + } + + + + /******************************************************************************* + ** Fluent setter for timestamp + ** + *******************************************************************************/ + public OutboundAPILogHeader withTimestamp(Instant timestamp) + { + this.timestamp = timestamp; + return (this); + } + + + + /******************************************************************************* + ** Getter for method + ** + *******************************************************************************/ + public String getMethod() + { + return method; + } + + + + /******************************************************************************* + ** Setter for method + ** + *******************************************************************************/ + public void setMethod(String method) + { + this.method = method; + } + + + + /******************************************************************************* + ** Fluent setter for method + ** + *******************************************************************************/ + public OutboundAPILogHeader withMethod(String method) + { + this.method = method; + return (this); + } + + + + /******************************************************************************* + ** Getter for statusCode + ** + *******************************************************************************/ + public Integer getStatusCode() + { + return statusCode; + } + + + + /******************************************************************************* + ** Setter for statusCode + ** + *******************************************************************************/ + public void setStatusCode(Integer statusCode) + { + this.statusCode = statusCode; + } + + + + /******************************************************************************* + ** Fluent setter for statusCode + ** + *******************************************************************************/ + public OutboundAPILogHeader withStatusCode(Integer statusCode) + { + this.statusCode = statusCode; + return (this); + } + + + + /******************************************************************************* + ** Getter for url + ** + *******************************************************************************/ + public String getUrl() + { + return url; + } + + + + /******************************************************************************* + ** Setter for url + ** + *******************************************************************************/ + public void setUrl(String url) + { + this.url = url; + } + + + + /******************************************************************************* + ** Fluent setter for url + ** + *******************************************************************************/ + public OutboundAPILogHeader withUrl(String url) + { + this.url = url; + return (this); + } + + + + /******************************************************************************* + ** Getter for outboundAPILogRequestList + *******************************************************************************/ + public List getOutboundAPILogRequestList() + { + return (this.outboundAPILogRequestList); + } + + + + /******************************************************************************* + ** Setter for outboundAPILogRequestList + *******************************************************************************/ + public void setOutboundAPILogRequestList(List outboundAPILogRequestList) + { + this.outboundAPILogRequestList = outboundAPILogRequestList; + } + + + + /******************************************************************************* + ** Fluent setter for outboundAPILogRequestList + *******************************************************************************/ + public OutboundAPILogHeader withOutboundAPILogRequestList(List outboundAPILogRequestList) + { + this.outboundAPILogRequestList = outboundAPILogRequestList; + return (this); + } + + + + /******************************************************************************* + ** Getter for outboundAPILogResponseList + *******************************************************************************/ + public List getOutboundAPILogResponseList() + { + return (this.outboundAPILogResponseList); + } + + + + /******************************************************************************* + ** Setter for outboundAPILogResponseList + *******************************************************************************/ + public void setOutboundAPILogResponseList(List outboundAPILogResponseList) + { + this.outboundAPILogResponseList = outboundAPILogResponseList; + } + + + + /******************************************************************************* + ** Fluent setter for outboundAPILogResponseList + *******************************************************************************/ + public OutboundAPILogHeader withOutboundAPILogResponseList(List outboundAPILogResponseList) + { + this.outboundAPILogResponseList = outboundAPILogResponseList; + return (this); + } + +} + diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogMetaDataProvider.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogMetaDataProvider.java index 6ac85466..fb8c0a69 100644 --- a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogMetaDataProvider.java +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogMetaDataProvider.java @@ -22,21 +22,33 @@ package com.kingsrook.qqq.backend.module.api.model; +import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType; import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior; +import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn; +import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType; +import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource; import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType; +import com.kingsrook.qqq.backend.core.model.metadata.tables.Association; import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability; +import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin; import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; +import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess; +import com.kingsrook.qqq.backend.module.api.processes.MigrateOutboundAPILogExtractStep; +import com.kingsrook.qqq.backend.module.api.processes.MigrateOutboundAPILogLoadStep; +import com.kingsrook.qqq.backend.module.api.processes.MigrateOutboundAPILogTransformStep; /******************************************************************************* @@ -50,8 +62,15 @@ public class OutboundAPILogMetaDataProvider *******************************************************************************/ public static void defineAll(QInstance qInstance, String backendName, Consumer backendDetailEnricher) throws QException { - definePossibleValueSources(qInstance); - defineOutboundAPILogTable(qInstance, backendName, backendDetailEnricher); + definePossibleValueSources().forEach(pvs -> + { + if(qInstance.getPossibleValueSource(pvs.getName()) == null) + { + qInstance.addPossibleValueSource(pvs); + } + }); + + qInstance.addTable(defineOutboundAPILogTable(backendName, backendDetailEnricher)); } @@ -59,9 +78,71 @@ public class OutboundAPILogMetaDataProvider /******************************************************************************* ** *******************************************************************************/ - private static void definePossibleValueSources(QInstance instance) + public static void defineNewVersion(QInstance qInstance, String backendName, Consumer backendDetailEnricher) throws QException { - instance.addPossibleValueSource(new QPossibleValueSource() + definePossibleValueSources().forEach(pvs -> + { + if(qInstance.getPossibleValueSource(pvs.getName()) == null) + { + qInstance.addPossibleValueSource(pvs); + } + }); + + qInstance.addTable(defineOutboundAPILogHeaderTable(backendName, backendDetailEnricher)); + qInstance.addPossibleValueSource(defineOutboundAPILogHeaderPossibleValueSource()); + qInstance.addTable(defineOutboundAPILogRequestTable(backendName, backendDetailEnricher)); + qInstance.addTable(defineOutboundAPILogResponseTable(backendName, backendDetailEnricher)); + defineJoins().forEach(join -> qInstance.add(join)); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + public static void defineMigrationProcesses(QInstance qInstance, String sourceTableName) + { + qInstance.addProcess(StreamedETLWithFrontendProcess.processMetaDataBuilder() + .withName("migrateOutboundApiLogToHeaderChildProcess") + .withLabel("Migrate Outbound API Log Test to Header/Child") + .withIcon(new QIcon().withName("drive_file_move")) + .withTableName(sourceTableName) + .withSourceTable(sourceTableName) + .withDestinationTable(OutboundAPILogHeader.TABLE_NAME) + .withExtractStepClass(MigrateOutboundAPILogExtractStep.class) + .withTransformStepClass(MigrateOutboundAPILogTransformStep.class) + .withLoadStepClass(MigrateOutboundAPILogLoadStep.class) + .withReviewStepRecordFields(List.of( + new QFieldMetaData("url", QFieldType.INTEGER) + )) + .getProcessMetaData()); + + qInstance.addProcess(StreamedETLWithFrontendProcess.processMetaDataBuilder() + .withName("migrateOutboundApiLogToMongoDBProcess") + .withLabel("Migrate Outbound API Log Test to MongoDB") + .withIcon(new QIcon().withName("drive_file_move")) + .withTableName(sourceTableName) + .withSourceTable(sourceTableName) + .withDestinationTable(OutboundAPILog.TABLE_NAME + "MongoDB") + .withExtractStepClass(MigrateOutboundAPILogExtractStep.class) + .withTransformStepClass(MigrateOutboundAPILogTransformStep.class) + .withLoadStepClass(MigrateOutboundAPILogLoadStep.class) + .withReviewStepRecordFields(List.of( + new QFieldMetaData("url", QFieldType.INTEGER) + )) + .getProcessMetaData()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static List definePossibleValueSources() + { + List rs = new ArrayList<>(); + + rs.add(new QPossibleValueSource() .withName("outboundApiMethod") .withType(QPossibleValueSourceType.ENUM) .withEnumValues(List.of( @@ -72,7 +153,7 @@ public class OutboundAPILogMetaDataProvider new QPossibleValue<>("DELETE") ))); - instance.addPossibleValueSource(new QPossibleValueSource() + rs.add(new QPossibleValueSource() .withName("outboundApiStatusCode") .withType(QPossibleValueSourceType.ENUM) .withEnumValues(List.of( @@ -91,6 +172,8 @@ public class OutboundAPILogMetaDataProvider new QPossibleValue<>(503, "503 (Service Unavailable)"), new QPossibleValue<>(504, "500 (Gateway Timeout)") ))); + + return (rs); } @@ -98,9 +181,8 @@ public class OutboundAPILogMetaDataProvider /******************************************************************************* ** *******************************************************************************/ - private static void defineOutboundAPILogTable(QInstance qInstance, String backendName, Consumer backendDetailEnricher) throws QException + public static QTableMetaData defineOutboundAPILogTable(String backendName, Consumer backendDetailEnricher) throws QException { - QTableMetaData tableMetaData = new QTableMetaData() .withName(OutboundAPILog.TABLE_NAME) .withLabel("Outbound API Log") @@ -119,29 +201,8 @@ public class OutboundAPILogMetaDataProvider tableMetaData.getField("requestBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json"))); tableMetaData.getField("responseBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json"))); - tableMetaData.getField("method").withFieldAdornment(new FieldAdornment(AdornmentType.CHIP) - .withValue(AdornmentType.ChipValues.colorValue("GET", AdornmentType.ChipValues.COLOR_INFO)) - .withValue(AdornmentType.ChipValues.colorValue("POST", AdornmentType.ChipValues.COLOR_SUCCESS)) - .withValue(AdornmentType.ChipValues.colorValue("DELETE", AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue("PATCH", AdornmentType.ChipValues.COLOR_WARNING)) - .withValue(AdornmentType.ChipValues.colorValue("PUT", AdornmentType.ChipValues.COLOR_WARNING))); - - tableMetaData.getField("statusCode").withFieldAdornment(new FieldAdornment(AdornmentType.CHIP) - .withValue(AdornmentType.ChipValues.colorValue(200, AdornmentType.ChipValues.COLOR_SUCCESS)) - .withValue(AdornmentType.ChipValues.colorValue(201, AdornmentType.ChipValues.COLOR_SUCCESS)) - .withValue(AdornmentType.ChipValues.colorValue(204, AdornmentType.ChipValues.COLOR_SUCCESS)) - .withValue(AdornmentType.ChipValues.colorValue(207, AdornmentType.ChipValues.COLOR_INFO)) - .withValue(AdornmentType.ChipValues.colorValue(400, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(401, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(403, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(404, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(422, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(429, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(500, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(502, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(503, AdornmentType.ChipValues.COLOR_ERROR)) - .withValue(AdornmentType.ChipValues.colorValue(504, AdornmentType.ChipValues.COLOR_ERROR)) - ); + addChipAdornmentToMethodField(tableMetaData); + addChipAdornmentToStatusCodeField(tableMetaData); /////////////////////////////////////////// // these are the lengths of a MySQL TEXT // @@ -160,6 +221,210 @@ public class OutboundAPILogMetaDataProvider backendDetailEnricher.accept(tableMetaData); } - qInstance.addTable(tableMetaData); + return (tableMetaData); } + + + + /*************************************************************************** + ** + ***************************************************************************/ + private static void addChipAdornmentToStatusCodeField(QTableMetaData tableMetaData) + { + tableMetaData.getField("statusCode").withFieldAdornment(new FieldAdornment(AdornmentType.CHIP) + .withValue(AdornmentType.ChipValues.colorValue(200, AdornmentType.ChipValues.COLOR_SUCCESS)) + .withValue(AdornmentType.ChipValues.colorValue(201, AdornmentType.ChipValues.COLOR_SUCCESS)) + .withValue(AdornmentType.ChipValues.colorValue(204, AdornmentType.ChipValues.COLOR_SUCCESS)) + .withValue(AdornmentType.ChipValues.colorValue(207, AdornmentType.ChipValues.COLOR_INFO)) + .withValue(AdornmentType.ChipValues.colorValue(400, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(401, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(403, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(404, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(422, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(429, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(500, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(502, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(503, AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue(504, AdornmentType.ChipValues.COLOR_ERROR)) + ); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + private static void addChipAdornmentToMethodField(QTableMetaData tableMetaData) + { + tableMetaData.getField("method").withFieldAdornment(new FieldAdornment(AdornmentType.CHIP) + .withValue(AdornmentType.ChipValues.colorValue("GET", AdornmentType.ChipValues.COLOR_INFO)) + .withValue(AdornmentType.ChipValues.colorValue("POST", AdornmentType.ChipValues.COLOR_SUCCESS)) + .withValue(AdornmentType.ChipValues.colorValue("DELETE", AdornmentType.ChipValues.COLOR_ERROR)) + .withValue(AdornmentType.ChipValues.colorValue("PATCH", AdornmentType.ChipValues.COLOR_WARNING)) + .withValue(AdornmentType.ChipValues.colorValue("PUT", AdornmentType.ChipValues.COLOR_WARNING))); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static QTableMetaData defineOutboundAPILogHeaderTable(String backendName, Consumer backendDetailEnricher) throws QException + { + QTableMetaData tableMetaData = new QTableMetaData() + .withName(OutboundAPILogHeader.TABLE_NAME) + .withLabel("Outbound API Log Header/Child") + .withIcon(new QIcon().withName("data_object")) + .withBackendName(backendName) + .withRecordLabelFormat("%s") + .withRecordLabelFields("id") + .withPrimaryKeyField("id") + .withFieldsFromEntity(OutboundAPILogHeader.class) + .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id"))) + .withSection(new QFieldSection("request", new QIcon().withName("arrow_upward"), Tier.T2, List.of("method", "url", OutboundAPILogRequest.TABLE_NAME + ".requestBody"))) + .withSection(new QFieldSection("response", new QIcon().withName("arrow_downward"), Tier.T2, List.of("statusCode", OutboundAPILogResponse.TABLE_NAME + ".responseBody"))) + .withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("timestamp"))) + .withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE); + + // tableMetaData.getField(OutboundAPILogRequest.TABLE_NAME + ".requestBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json"))); + // tableMetaData.getField(OutboundAPILogResponse.TABLE_NAME + ".responseBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json"))); + + addChipAdornmentToMethodField(tableMetaData); + addChipAdornmentToStatusCodeField(tableMetaData); + + tableMetaData.withAssociation(new Association() + .withName(OutboundAPILogRequest.TABLE_NAME) + .withAssociatedTableName(OutboundAPILogRequest.TABLE_NAME) + .withJoinName(QJoinMetaData.makeInferredJoinName(OutboundAPILogHeader.TABLE_NAME, OutboundAPILogRequest.TABLE_NAME))); + + tableMetaData.withAssociation(new Association() + .withName(OutboundAPILogResponse.TABLE_NAME) + .withAssociatedTableName(OutboundAPILogResponse.TABLE_NAME) + .withJoinName(QJoinMetaData.makeInferredJoinName(OutboundAPILogHeader.TABLE_NAME, OutboundAPILogResponse.TABLE_NAME))); + + tableMetaData.withExposedJoin(new ExposedJoin() + .withJoinTable(OutboundAPILogRequest.TABLE_NAME) + .withJoinPath(List.of(QJoinMetaData.makeInferredJoinName(OutboundAPILogHeader.TABLE_NAME, OutboundAPILogRequest.TABLE_NAME)))); + + tableMetaData.withExposedJoin(new ExposedJoin() + .withJoinTable(OutboundAPILogResponse.TABLE_NAME) + .withJoinPath(List.of(QJoinMetaData.makeInferredJoinName(OutboundAPILogHeader.TABLE_NAME, OutboundAPILogResponse.TABLE_NAME)))); + + tableMetaData.getField("url").withMaxLength(4096).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS); + tableMetaData.getField("url").withFieldAdornment(AdornmentType.Size.XLARGE.toAdornment()); + + if(backendDetailEnricher != null) + { + backendDetailEnricher.accept(tableMetaData); + } + + return (tableMetaData); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static QTableMetaData defineOutboundAPILogRequestTable(String backendName, Consumer backendDetailEnricher) throws QException + { + QTableMetaData tableMetaData = new QTableMetaData() + .withName(OutboundAPILogRequest.TABLE_NAME) + .withLabel("Outbound API Log Request") + .withIcon(new QIcon().withName("arrow_upward")) + .withBackendName(backendName) + .withRecordLabelFormat("%s") + .withRecordLabelFields("id") + .withPrimaryKeyField("id") + .withFieldsFromEntity(OutboundAPILogRequest.class) + .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "outboundApiLogHeaderId"))) + .withSection(new QFieldSection("request", new QIcon().withName("arrow_upward"), Tier.T2, List.of("requestBody"))) + .withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE); + + tableMetaData.getField("requestBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json"))); + + ////////////////////////////////////////////// + // this is the length of a MySQL MEDIUMTEXT // + ////////////////////////////////////////////// + tableMetaData.getField("requestBody").withMaxLength(16_777_215).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS); + + if(backendDetailEnricher != null) + { + backendDetailEnricher.accept(tableMetaData); + } + + return (tableMetaData); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static QTableMetaData defineOutboundAPILogResponseTable(String backendName, Consumer backendDetailEnricher) throws QException + { + QTableMetaData tableMetaData = new QTableMetaData() + .withName(OutboundAPILogResponse.TABLE_NAME) + .withLabel("Outbound API Log Response") + .withIcon(new QIcon().withName("arrow_upward")) + .withBackendName(backendName) + .withRecordLabelFormat("%s") + .withRecordLabelFields("id") + .withPrimaryKeyField("id") + .withFieldsFromEntity(OutboundAPILogResponse.class) + .withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "outboundApiLogHeaderId"))) + .withSection(new QFieldSection("response", new QIcon().withName("arrow_upward"), Tier.T2, List.of("responseBody"))) + .withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE); + + tableMetaData.getField("responseBody").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json"))); + + ////////////////////////////////////////////// + // this is the length of a MySQL MEDIUMTEXT // + ////////////////////////////////////////////// + tableMetaData.getField("responseBody").withMaxLength(16_777_215).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS); + + if(backendDetailEnricher != null) + { + backendDetailEnricher.accept(tableMetaData); + } + + return (tableMetaData); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static List defineJoins() + { + List rs = new ArrayList<>(); + + rs.add(new QJoinMetaData() + .withLeftTable(OutboundAPILogHeader.TABLE_NAME) + .withRightTable(OutboundAPILogRequest.TABLE_NAME) + .withInferredName() + .withType(JoinType.ONE_TO_ONE) + .withJoinOn(new JoinOn("id", "outboundApiLogHeaderId"))); + + rs.add(new QJoinMetaData() + .withLeftTable(OutboundAPILogHeader.TABLE_NAME) + .withRightTable(OutboundAPILogResponse.TABLE_NAME) + .withInferredName() + .withType(JoinType.ONE_TO_ONE) + .withJoinOn(new JoinOn("id", "outboundApiLogHeaderId"))); + + return (rs); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + private static QPossibleValueSource defineOutboundAPILogHeaderPossibleValueSource() + { + return QPossibleValueSource.newForTable(OutboundAPILogHeader.TABLE_NAME); + } + } diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogRequest.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogRequest.java new file mode 100644 index 00000000..665f7d5d --- /dev/null +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogRequest.java @@ -0,0 +1,165 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.qqq.backend.module.api.model; + + +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.data.QField; +import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; + + +/******************************************************************************* + ** Entity bean for OutboundApiLogRequest table + *******************************************************************************/ +public class OutboundAPILogRequest extends QRecordEntity +{ + public static final String TABLE_NAME = "outboundApiLogRequest"; + + @QField(isEditable = false) + private Integer id; + + @QField(possibleValueSourceName = OutboundAPILogHeader.TABLE_NAME) + private Integer outboundApiLogHeaderId; + + @QField() + private String requestBody; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public OutboundAPILogRequest() + { + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public OutboundAPILogRequest(QRecord qRecord) throws QException + { + populateFromQRecord(qRecord); + } + + + + /******************************************************************************* + ** Getter for id + ** + *******************************************************************************/ + public Integer getId() + { + return id; + } + + + + /******************************************************************************* + ** Setter for id + ** + *******************************************************************************/ + public void setId(Integer id) + { + this.id = id; + } + + + + /******************************************************************************* + ** Fluent setter for id + ** + *******************************************************************************/ + public OutboundAPILogRequest withId(Integer id) + { + this.id = id; + return (this); + } + + + + /******************************************************************************* + ** Getter for requestBody + *******************************************************************************/ + public String getRequestBody() + { + return (this.requestBody); + } + + + + /******************************************************************************* + ** Setter for requestBody + *******************************************************************************/ + public void setRequestBody(String requestBody) + { + this.requestBody = requestBody; + } + + + + /******************************************************************************* + ** Fluent setter for requestBody + *******************************************************************************/ + public OutboundAPILogRequest withRequestBody(String requestBody) + { + this.requestBody = requestBody; + return (this); + } + + + + /******************************************************************************* + ** Getter for outboundApiLogHeaderId + *******************************************************************************/ + public Integer getOutboundApiLogHeaderId() + { + return (this.outboundApiLogHeaderId); + } + + + + /******************************************************************************* + ** Setter for outboundApiLogHeaderId + *******************************************************************************/ + public void setOutboundApiLogHeaderId(Integer outboundApiLogHeaderId) + { + this.outboundApiLogHeaderId = outboundApiLogHeaderId; + } + + + + /******************************************************************************* + ** Fluent setter for outboundApiLogHeaderId + *******************************************************************************/ + public OutboundAPILogRequest withOutboundApiLogHeaderId(Integer outboundApiLogHeaderId) + { + this.outboundApiLogHeaderId = outboundApiLogHeaderId; + return (this); + } + +} + diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogResponse.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogResponse.java new file mode 100644 index 00000000..a28697a4 --- /dev/null +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/model/OutboundAPILogResponse.java @@ -0,0 +1,165 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. 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.qqq.backend.module.api.model; + + +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.data.QField; +import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.model.data.QRecordEntity; + + +/******************************************************************************* + ** Entity bean for OutboundApiLogResponse table + *******************************************************************************/ +public class OutboundAPILogResponse extends QRecordEntity +{ + public static final String TABLE_NAME = "outboundApiLogResponse"; + + @QField(isEditable = false) + private Integer id; + + @QField(possibleValueSourceName = OutboundAPILogHeader.TABLE_NAME) + private Integer outboundApiLogHeaderId; + + @QField() + private String responseBody; + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public OutboundAPILogResponse() + { + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public OutboundAPILogResponse(QRecord qRecord) throws QException + { + populateFromQRecord(qRecord); + } + + + + /******************************************************************************* + ** Getter for id + ** + *******************************************************************************/ + public Integer getId() + { + return id; + } + + + + /******************************************************************************* + ** Setter for id + ** + *******************************************************************************/ + public void setId(Integer id) + { + this.id = id; + } + + + + /******************************************************************************* + ** Fluent setter for id + ** + *******************************************************************************/ + public OutboundAPILogResponse withId(Integer id) + { + this.id = id; + return (this); + } + + + + /******************************************************************************* + ** Getter for responseBody + *******************************************************************************/ + public String getResponseBody() + { + return (this.responseBody); + } + + + + /******************************************************************************* + ** Setter for responseBody + *******************************************************************************/ + public void setResponseBody(String responseBody) + { + this.responseBody = responseBody; + } + + + + /******************************************************************************* + ** Fluent setter for responseBody + *******************************************************************************/ + public OutboundAPILogResponse withResponseBody(String responseBody) + { + this.responseBody = responseBody; + return (this); + } + + + + /******************************************************************************* + ** Getter for outboundApiLogHeaderId + *******************************************************************************/ + public Integer getOutboundApiLogHeaderId() + { + return (this.outboundApiLogHeaderId); + } + + + + /******************************************************************************* + ** Setter for outboundApiLogHeaderId + *******************************************************************************/ + public void setOutboundApiLogHeaderId(Integer outboundApiLogHeaderId) + { + this.outboundApiLogHeaderId = outboundApiLogHeaderId; + } + + + + /******************************************************************************* + ** Fluent setter for outboundApiLogHeaderId + *******************************************************************************/ + public OutboundAPILogResponse withOutboundApiLogHeaderId(Integer outboundApiLogHeaderId) + { + this.outboundApiLogHeaderId = outboundApiLogHeaderId; + return (this); + } + +} + diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogExtractStep.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogExtractStep.java new file mode 100644 index 00000000..d6d5c436 --- /dev/null +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogExtractStep.java @@ -0,0 +1,45 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. 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.qqq.backend.module.api.processes; + + +import com.kingsrook.qqq.backend.core.model.actions.tables.QueryHint; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; +import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class MigrateOutboundAPILogExtractStep extends ExtractViaQueryStep +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + protected void customizeInputPreQuery(QueryInput queryInput) + { + queryInput.withQueryHint(QueryHint.POTENTIALLY_LARGE_NUMBER_OF_RESULTS); + } + +} diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogLoadStep.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogLoadStep.java new file mode 100644 index 00000000..c433b1b2 --- /dev/null +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogLoadStep.java @@ -0,0 +1,54 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. 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.qqq.backend.module.api.processes; + + +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.RunBackendStepInput; +import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput; +import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.LoadViaInsertStep; + + +/******************************************************************************* + ** store outboundApiLogHeaders and maybe more... + *******************************************************************************/ +public class MigrateOutboundAPILogLoadStep extends LoadViaInsertStep +{ + private static final QLogger LOG = QLogger.getLogger(MigrateOutboundAPILogLoadStep.class); + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException + { + super.runOnePage(runBackendStepInput, runBackendStepOutput); + + /////////////////////////////////////// + // todo - track what we've migrated? // + /////////////////////////////////////// + } + +} diff --git a/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogTransformStep.java b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogTransformStep.java new file mode 100644 index 00000000..1697eb32 --- /dev/null +++ b/qqq-backend-module-api/src/main/java/com/kingsrook/qqq/backend/module/api/processes/MigrateOutboundAPILogTransformStep.java @@ -0,0 +1,127 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. 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.qqq.backend.module.api.processes; + + +import java.util.ArrayList; +import java.util.List; +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.data.QRecord; +import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.AbstractTransformStep; +import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess; +import com.kingsrook.qqq.backend.core.processes.implementations.general.StandardProcessSummaryLineProducer; +import com.kingsrook.qqq.backend.module.api.model.OutboundAPILogHeader; +import com.kingsrook.qqq.backend.module.api.model.OutboundAPILogRequest; +import com.kingsrook.qqq.backend.module.api.model.OutboundAPILogResponse; + + +/******************************************************************************* + ** migrate records from original (singular) outboundApiLog table to new split-up + ** version (outboundApiLogHeader) + *******************************************************************************/ +public class MigrateOutboundAPILogTransformStep extends AbstractTransformStep +{ + private static final QLogger LOG = QLogger.getLogger(MigrateOutboundAPILogTransformStep.class); + + private ProcessSummaryLine okToInsertLine = StandardProcessSummaryLineProducer.getOkToInsertLine(); + private ProcessSummaryLine errorLine = StandardProcessSummaryLineProducer.getErrorLine(); + + + + /*************************************************************************** + ** + ***************************************************************************/ + /* + @Override + public Integer getOverrideRecordPipeCapacity(RunBackendStepInput runBackendStepInput) + { + return (100); + } + */ + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public ArrayList getProcessSummary(RunBackendStepOutput runBackendStepOutput, boolean isForResultScreen) + { + ArrayList rs = new ArrayList<>(); + okToInsertLine.addSelfToListIfAnyCount(rs); + errorLine.addSelfToListIfAnyCount(rs); + return (rs); + } + + + + /*************************************************************************** + ** + ***************************************************************************/ + @Override + public void preRun(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException + { + runBackendStepOutput.addValue("counter", 0); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public void runOnePage(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException + { + int counter = runBackendStepOutput.getValueInteger("counter") + runBackendStepInput.getRecords().size(); + runBackendStepOutput.addValue("counter", counter); + runBackendStepInput.getAsyncJobCallback().updateStatus("Migrating records (at #" + String.format("%,d", counter) + ")"); + + String destinationTable = runBackendStepInput.getValueString(StreamedETLWithFrontendProcess.FIELD_DESTINATION_TABLE); + + for(QRecord record : runBackendStepInput.getRecords()) + { + okToInsertLine.incrementCountAndAddPrimaryKey(record.getValue("id")); + + if(destinationTable.equals(OutboundAPILogHeader.TABLE_NAME)) + { + OutboundAPILogHeader outboundAPILogHeader = new OutboundAPILogHeader(record); + outboundAPILogHeader.withOutboundAPILogRequestList(List.of(new OutboundAPILogRequest().withRequestBody(record.getValueString("requestBody")))); + outboundAPILogHeader.withOutboundAPILogResponseList(List.of(new OutboundAPILogResponse().withResponseBody(record.getValueString("responseBody")))); + runBackendStepOutput.addRecord(outboundAPILogHeader.toQRecord()); + } + else + { + /////////////////////////////////////////////////////////////////// + // for the mongodb migration, just pass records straight through // + /////////////////////////////////////////////////////////////////// + record.setValue("id", null); + runBackendStepOutput.addRecord(record); + } + } + } + +}