/* * 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.dataanalysis; import com.kingsrook.qbits.dataanalysis.api.DataAnalysisApi; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerMultiOutput; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.permissions.TableFeaturePermission; import com.kingsrook.qqq.backend.core.model.metadata.permissions.TableFeaturePermissionsMetaData; import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaData; import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaDataProducer; import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider; /******************************************************************************* ** Entry point for the Data Analysis qbit; scans only its metadata package. *******************************************************************************/ public class DataAnalysisQBitProducer implements QBitMetaDataProducer { public static final String GROUP_ID = "com.kingsrook.qbits"; public static final String ARTIFACT_ID = "qbit-data-analysis"; public static final String VERSION = "0.1.0"; private DataAnalysisQBitConfig qBitConfig; private String namespace; /******************************************************************************* ** Fluent configuration setter. *******************************************************************************/ public DataAnalysisQBitProducer withQBitConfig(DataAnalysisQBitConfig config) { qBitConfig = config; return (this); } /******************************************************************************* ** Configuration for component producers. *******************************************************************************/ @Override public DataAnalysisQBitConfig getQBitConfig() { return (qBitConfig); } /******************************************************************************* ** Fluent namespace setter. *******************************************************************************/ public DataAnalysisQBitProducer withNamespace(String namespace) { this.namespace = namespace; return (this); } /******************************************************************************* ** Qbit namespace. *******************************************************************************/ @Override public String getNamespace() { return (namespace); } /******************************************************************************* ** Qbit identity. *******************************************************************************/ @Override public QBitMetaData getQBitMetaData() { return (new QBitMetaData().withGroupId(GROUP_ID).withArtifactId(ARTIFACT_ID).withVersion(VERSION).withNamespace(namespace).withConfig(qBitConfig)); } /******************************************************************************* ** Unconfigured producers discovered by host scanning are disabled. *******************************************************************************/ @Override public boolean isEnabled() { return (qBitConfig != null); } /******************************************************************************* ** Helpers, renderers and the top-level producer are never component producers. *******************************************************************************/ @Override public String getPackageNameForFindingMetaDataProducers() { return ("com.kingsrook.qbits.dataanalysis.metadata"); } /******************************************************************************* ** Install metadata and attach the app to its optional parent, like reference qbits. *******************************************************************************/ @Override public MetaDataProducerMultiOutput produce(QInstance instance) throws QException { qBitConfig.validate(instance); if(instance.getPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.NAME) == null) { instance.addPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(instance)); } MetaDataProducerMultiOutput output = QBitMetaDataProducer.super.produce(instance); output.addSelfToInstance(instance); new DataAnalysisRuntime(qBitConfig).addSelfToInstance(instance); com.kingsrook.qqq.backend.core.alerts.AlertsInstanceMetaData.ofOrWithNew(instance).withAlertType( new com.kingsrook.qqq.backend.core.alerts.QAlertTypeMetaData().withName(com.kingsrook.qbits.dataanalysis.watch.WatchEvaluator.ALERT_TYPE) .withLabel("Data anomaly").withSourceName(ARTIFACT_ID).withGroupName("data-analysis") .withDefaultSeverity(com.kingsrook.qqq.backend.core.alerts.AlertSeverity.WARNING) .withResolveMode(com.kingsrook.qqq.backend.core.alerts.ResolveMode.PRODUCER) .withSubjectTableName(com.kingsrook.qbits.dataanalysis.model.DataAnalysisTables.WATCH) .withVisibilityPermission("data_analysis.watch.read")); com.kingsrook.qqq.backend.core.alerts.AlertsInstanceMetaData.ofOrWithNew(instance).withAlertType( new com.kingsrook.qqq.backend.core.alerts.QAlertTypeMetaData().withName(com.kingsrook.qbits.dataanalysis.schedule.ScheduleRunner.ALERT_TYPE) .withLabel("Analysis schedule failed").withSourceName(ARTIFACT_ID).withGroupName("data-analysis") .withDefaultSeverity(com.kingsrook.qqq.backend.core.alerts.AlertSeverity.ERROR) .withResolveMode(com.kingsrook.qqq.backend.core.alerts.ResolveMode.PRODUCER) .withSubjectTableName(com.kingsrook.qbits.dataanalysis.model.DataAnalysisTables.SCHEDULE) .withVisibilityPermission("data_analysis.saved.read")); var included = java.util.Set.copyOf(qBitConfig.getIncludeTables()); var excluded = java.util.Set.copyOf(qBitConfig.getExcludeTables()); TableFeaturePermissionsMetaData.ofOrWithNew(instance).withPermission(new TableFeaturePermission() .withSuffix("analyze").withLabel("Analyze").withImpliedBy(null) .withAppliesTo((qInstance, table) -> !table.getName().startsWith(DataAnalysisQBitConfig.TABLE_NAME_PREFIX) && (included.isEmpty() || included.contains(table.getName())) && !excluded.contains(table.getName()))); try { Class.forName("io.javalin.Javalin"); Class.forName("com.kingsrook.qqq.middleware.javalin.QJavalinImplementation"); DataAnalysisApi.register(instance); } catch(ClassNotFoundException expected) { // The engine and metadata also work without the optional middleware. } if(qBitConfig.getParentAppName() != null && instance.getApp(qBitConfig.getParentAppName()) != null) { instance.getApp(qBitConfig.getParentAppName()).withChild(instance.getApp(qBitConfig.getAppName())); } return (new MetaDataProducerMultiOutput()); } }