Files
qbit-edi/src/main/java/com/kingsrook/qbits/edi/EdiQBitConfig.java
james.maesandClaude Opus 5.5 cccd0ac0ab fix(app): business-only EDI home; system widgets for the Admin home
Move System, Dead letters, Certificates expiring and Setup status off the
EDI home into EdiHome.ADMIN_WIDGET_NAMES, gated to edi.admin.hasAccess
(hidden when denied) and full width, for a host to place on its Admin
home. Every remaining home widget requires edi.hasAccess (hidden when
denied); none is NOT_PROTECTED.

Add "Inbound documents are delayed": an alert at the top of the home,
shown only when the oldest unprocessed inbound document is older than
inboundDelayedAfterMinutes (default 60), with its age and a link to the
filtered inbound documents.

Setup counts that fail show "Unavailable" and are logged, never -1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:28:46 -05:00

417 lines
15 KiB
Java

/*
* 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/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.kingsrook.qbits.edi;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitConfig;
/*******************************************************************************
** Code configuration for the EDI qbit. Runtime settings (partners, agreements,
** transports, guides and maps) are data in the edi_ tables, not here.
*******************************************************************************/
public class EdiQBitConfig implements QBitConfig
{
public static final String TABLE_NAME_PREFIX = "edi_";
public static final String PERMISSION_BASE_NAME = "edi";
private final java.util.Set<String> sendAsEdiTables = new java.util.LinkedHashSet<>();
/*******************************************************************************
** Opt a source table into the manual Send As EDI action.
*******************************************************************************/
public EdiQBitConfig withSendAsEdiTable(String tableName)
{
sendAsEdiTables.add(tableName);
return (this);
}
/*******************************************************************************
** Explicitly opted-in manual source tables.
*******************************************************************************/
public java.util.Set<String> getSendAsEdiTables()
{
return (java.util.Set.copyOf(sendAsEdiTables));
}
private com.kingsrook.qbits.edi.guide.ExternalCodeSetProvider externalCodeSetProvider;
/*******************************************************************************
** Optional host provider for licensed external code sets.
*******************************************************************************/
@JsonIgnore
public com.kingsrook.qbits.edi.guide.ExternalCodeSetProvider getExternalCodeSetProvider()
{
return (externalCodeSetProvider);
}
/*******************************************************************************
** Register a provider; null retains format-only validation.
*******************************************************************************/
public EdiQBitConfig withExternalCodeSetProvider(com.kingsrook.qbits.edi.guide.ExternalCodeSetProvider value)
{
externalCodeSetProvider = value;
return (this);
}
private String payloadBucketAlias = "edi";
private java.util.function.Supplier<com.kingsrook.qqq.backend.core.model.session.QSession> serviceSessionSupplier = null;
private java.time.Clock clock = java.time.Clock.systemUTC();
private String esbDestinationName;
private String schedulerName;
private Integer maxSegmentsPerTransaction = 100000;
private String payloadStore = "DATABASE";
private com.kingsrook.qbits.edi.security.EdiSecrets secretsResolver = System::getenv;
private Boolean partnerLocks = false;
private String backendName;
private String appName = "edi";
private String appLabel = "EDI";
private String parentAppName;
private String publicBaseUrl;
private Integer inboundDelayedAfterMinutes = 60;
/*******************************************************************************
** Validate the configuration, collecting all errors.
*******************************************************************************/
@Override
public void validate(QInstance instance, List<String> errors)
{
if(backendName == null || instance.getBackend(backendName) == null)
{
errors.add("backendName must name an existing backend");
}
if(maxSegmentsPerTransaction == null || maxSegmentsPerTransaction < 2)
{
errors.add("maxSegmentsPerTransaction must be at least 2");
}
if(!List.of("DATABASE", "OBJECT_STORAGE").contains(payloadStore))
{
errors.add("payloadStore must be DATABASE or OBJECT_STORAGE");
}
if(secretsResolver == null || partnerLocks == null)
{
errors.add("secretsResolver and partnerLocks are required");
}
if(inboundDelayedAfterMinutes == null || inboundDelayedAfterMinutes < 1)
{
errors.add("inboundDelayedAfterMinutes must be at least 1");
}
if(appName == null || appName.isBlank())
{
errors.add("appName is required");
}
}
/*******************************************************************************
** The backend for the qbit's tables.
*******************************************************************************/
@Override
public String getDefaultBackendNameForTables()
{
return (backendName);
}
/*******************************************************************************
** Getter for backendName
*******************************************************************************/
public String getBackendName()
{
return (this.backendName);
}
/*******************************************************************************
** Fluent setter for backendName
*******************************************************************************/
public EdiQBitConfig withBackendName(String backendName)
{
this.backendName = backendName;
return (this);
}
/*******************************************************************************
** Getter for appName
*******************************************************************************/
public String getAppName()
{
return (this.appName);
}
/*******************************************************************************
** Fluent setter for appName
*******************************************************************************/
public EdiQBitConfig withAppName(String appName)
{
this.appName = appName;
return (this);
}
/*******************************************************************************
** Getter for appLabel
*******************************************************************************/
public String getAppLabel()
{
return (this.appLabel);
}
/*******************************************************************************
** Fluent setter for appLabel
*******************************************************************************/
public EdiQBitConfig withAppLabel(String appLabel)
{
this.appLabel = appLabel;
return (this);
}
/*******************************************************************************
** Getter for parentAppName
*******************************************************************************/
public String getParentAppName()
{
return (this.parentAppName);
}
/*******************************************************************************
** Fluent setter for parentAppName
*******************************************************************************/
public EdiQBitConfig withParentAppName(String parentAppName)
{
this.parentAppName = parentAppName;
return (this);
}
/*******************************************************************************
** Get schedulerName.
*******************************************************************************/
public String getSchedulerName()
{
return (schedulerName);
}
/*******************************************************************************
** Configure schedulerName.
*******************************************************************************/
public EdiQBitConfig withSchedulerName(String value)
{
schedulerName = value;
return (this);
}
/*******************************************************************************
** Get maxSegmentsPerTransaction.
*******************************************************************************/
public Integer getMaxSegmentsPerTransaction()
{
return (maxSegmentsPerTransaction);
}
/*******************************************************************************
** Configure maxSegmentsPerTransaction.
*******************************************************************************/
public EdiQBitConfig withMaxSegmentsPerTransaction(Integer value)
{
maxSegmentsPerTransaction = value;
return (this);
}
/*******************************************************************************
** Get payloadStore.
*******************************************************************************/
@JsonIgnore
public String getPayloadStore()
{
return (payloadStore);
}
/*******************************************************************************
** Configure payloadStore.
*******************************************************************************/
public EdiQBitConfig withPayloadStore(String value)
{
payloadStore = value;
return (this);
}
/*******************************************************************************
** Get secretsResolver.
*******************************************************************************/
@JsonIgnore
public com.kingsrook.qbits.edi.security.EdiSecrets getSecretsResolver()
{
return (secretsResolver);
}
/*******************************************************************************
** Configure secretsResolver.
*******************************************************************************/
public EdiQBitConfig withSecretsResolver(com.kingsrook.qbits.edi.security.EdiSecrets value)
{
secretsResolver = value;
return (this);
}
/*******************************************************************************
** Get partnerLocks.
*******************************************************************************/
public Boolean getPartnerLocks()
{
return (partnerLocks);
}
/*******************************************************************************
** Configure partnerLocks.
*******************************************************************************/
public EdiQBitConfig withPartnerLocks(Boolean value)
{
partnerLocks = value;
return (this);
}
/*******************************************************************************
** Get payloadBucketAlias.
*******************************************************************************/
public String getPayloadBucketAlias()
{
return (payloadBucketAlias);
}
/*******************************************************************************
** Configure payloadBucketAlias.
*******************************************************************************/
public EdiQBitConfig withPayloadBucketAlias(String value)
{
payloadBucketAlias = value;
return (this);
}
/*******************************************************************************
** Get serviceSessionSupplier.
*******************************************************************************/
@JsonIgnore
public java.util.function.Supplier<com.kingsrook.qqq.backend.core.model.session.QSession> getServiceSessionSupplier()
{
return (serviceSessionSupplier);
}
/*******************************************************************************
** Configure serviceSessionSupplier.
*******************************************************************************/
public EdiQBitConfig withServiceSessionSupplier(java.util.function.Supplier<com.kingsrook.qqq.backend.core.model.session.QSession> value)
{
serviceSessionSupplier = value;
return (this);
}
/*******************************************************************************
** Get clock.
*******************************************************************************/
@JsonIgnore
public java.time.Clock getClock()
{
return (clock);
}
/*******************************************************************************
** Configure clock.
*******************************************************************************/
public EdiQBitConfig withClock(java.time.Clock value)
{
clock = value;
return (this);
}
/*******************************************************************************
** Optional destination for core table events on main tables.
*******************************************************************************/
public String getEsbDestinationName()
{
return (esbDestinationName);
}
/*******************************************************************************
** Enable standard table publications to a host-configured destination.
*******************************************************************************/
public EdiQBitConfig withEsbDestinationName(String value)
{
esbDestinationName = value;
return (this);
}
/*******************************************************************************
** Public base URL of this deployment (for example https://edi.example.com),
** used to build the asynchronous AS2 receipt address
** {base}/qqq/hooks/edi/as2/{transport}/mdn.
*******************************************************************************/
public String getPublicBaseUrl()
{
return (publicBaseUrl);
}
/*******************************************************************************
** Set the public base URL.
*******************************************************************************/
public EdiQBitConfig withPublicBaseUrl(String value)
{
publicBaseUrl = value;
return (this);
}
/*******************************************************************************
** How old (in minutes) the oldest unprocessed inbound document may get before
** the EDI home says "Inbound documents are delayed". Default 60.
*******************************************************************************/
public Integer getInboundDelayedAfterMinutes()
{
return (inboundDelayedAfterMinutes);
}
/*******************************************************************************
** Set the inbound delay threshold in minutes.
*******************************************************************************/
public EdiQBitConfig withInboundDelayedAfterMinutes(Integer value)
{
inboundDelayedAfterMinutes = value;
return (this);
}
}