From 93df3f4ce4e9b4e04d2a69e319fb3cc641e868d9 Mon Sep 17 00:00:00 2001
From: Matt Magoffin
Date: Mon, 28 Oct 2024 16:48:49 +1300
Subject: [PATCH 01/11] NET-414: cloud integrations settings API.
Squashed commit of the following:
commit 06d81efd4425bfe8f16e9ed75e342699a1564b89
Author: Matt Magoffin
Date: Mon Oct 28 16:48:08 2024 +1300
NET-414: work on cloud datum stream settings API.
commit e8f2ddf424e1627965810eb988c5259f28d51277
Author: Matt Magoffin
Date: Mon Oct 28 16:20:33 2024 +1300
NET-414: work on cloud user settings API.
commit 4ae1b30228ddad52794f8617d4e9b7f1732dfe56
Author: Matt Magoffin
Date: Mon Oct 28 14:40:48 2024 +1300
NET-414: integrate datum stream settings into poll task service.
commit 4cce9382070e5571e436a170bf1df40b655d1cbb
Author: Matt Magoffin
Date: Mon Oct 28 14:22:12 2024 +1300
Remove unused variable.
commit d8da11ebc072b607c3afc1c9c8ca0c20258676be
Author: Matt Magoffin
Date: Mon Oct 28 11:14:57 2024 +1300
NET-414: start work on datum stream publish settings support.
---
.../NET-414-cloud-integrations-flux.sql | 44 +++
.../impl/DaoCloudDatumStreamPollService.java | 58 ++-
.../config/CloudIntegrationsDaoConfig.java | 36 +-
.../central/c2c/dao/BasicFilter.java | 4 +-
.../CloudDatumStreamSettingsEntityDao.java | 57 +++
.../dao/CloudDatumStreamSettingsFilter.java | 34 ++
.../c2c/dao/UserSettingsEntityDao.java | 36 ++
...oudDatumStreamSettingsEntityRowMapper.java | 90 +++++
...JdbcCloudDatumStreamSettingsEntityDao.java | 151 ++++++++
.../dao/jdbc/JdbcUserSettingsEntityDao.java | 91 +++++
.../dao/jdbc/UserSettingsEntityRowMapper.java | 86 +++++
.../jdbc/sql/DeleteUserSettingsEntity.java | 71 ++++
.../SelectCloudDatumStreamSettingsEntity.java | 245 +++++++++++++
.../jdbc/sql/SelectUserSettingsEntity.java | 74 ++++
.../UpsertCloudDatumStreamSettingsEntity.java | 100 ++++++
.../jdbc/sql/UpsertUserSettingsEntity.java | 93 +++++
.../domain/BasicCloudDatumStreamSettings.java | 48 +++
.../c2c/domain/CloudDatumStreamSettings.java | 49 +++
.../CloudDatumStreamSettingsEntity.java | 169 +++++++++
.../c2c/domain/UserSettingsEntity.java | 196 ++++++++++
.../DaoCloudDatumStreamPollServiceTests.java | 18 +-
.../c2c/dao/jdbc/test/CinJdbcTestUtils.java | 82 ++++-
...loudDatumStreamSettingsEntityDaoTests.java | 340 ++++++++++++++++++
.../test/JdbcUserSettingsEntityDaoTests.java | 145 ++++++++
.../central/test/CommonTestUtils.java | 12 +-
...loudIntegrationsDatumStreamPollConfig.java | 8 +-
.../v1/UserCloudIntegrationsController.java | 70 +++-
.../UserCloudIntegrationsSecurityAspect.java | 45 +++
.../c2c/biz/UserCloudIntegrationsBiz.java | 43 ++-
.../biz/impl/DaoUserCloudIntegrationsBiz.java | 82 ++++-
.../UserCloudIntegrationsBizConfig.java | 16 +-
.../CloudDatumStreamSettingsEntityInput.java | 88 +++++
.../c2c/domain/UserSettingsEntityInput.java | 94 +++++
.../DaoUserCloudIntegrationsBizTests.java | 109 +++++-
34 files changed, 2853 insertions(+), 31 deletions(-)
create mode 100644 solarnet-db-setup/postgres/updates/NET-414-cloud-integrations-flux.sql
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsEntityDao.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsFilter.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/UserSettingsEntityDao.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/CloudDatumStreamSettingsEntityRowMapper.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcCloudDatumStreamSettingsEntityDao.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcUserSettingsEntityDao.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/UserSettingsEntityRowMapper.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/DeleteUserSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectCloudDatumStreamSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectUserSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertCloudDatumStreamSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertUserSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/BasicCloudDatumStreamSettings.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettings.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/UserSettingsEntity.java
create mode 100644 solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/dao/jdbc/test/JdbcCloudDatumStreamSettingsEntityDaoTests.java
create mode 100644 solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/dao/jdbc/test/JdbcUserSettingsEntityDaoTests.java
create mode 100644 solarnet/user-cloud-integrations/src/main/java/net/solarnetwork/central/user/c2c/domain/CloudDatumStreamSettingsEntityInput.java
create mode 100644 solarnet/user-cloud-integrations/src/main/java/net/solarnetwork/central/user/c2c/domain/UserSettingsEntityInput.java
diff --git a/solarnet-db-setup/postgres/updates/NET-414-cloud-integrations-flux.sql b/solarnet-db-setup/postgres/updates/NET-414-cloud-integrations-flux.sql
new file mode 100644
index 000000000..0dff0ce14
--- /dev/null
+++ b/solarnet-db-setup/postgres/updates/NET-414-cloud-integrations-flux.sql
@@ -0,0 +1,44 @@
+/**
+ * Cloud integration user (account) configuration.
+ *
+ * @column user_id the ID of the account owner
+ * @column id the ID of the configuration
+ * @column created the creation date
+ * @column modified the modification date
+ * @column pub_in a flag to publish datum streams to SolarIn
+ * @column pub_flux a flag to publish datum streams to SolarFlux
+ */
+CREATE TABLE solardin.cin_user_settings (
+ user_id BIGINT NOT NULL,
+ created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ modified TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ pub_in BOOLEAN NOT NULL DEFAULT TRUE,
+ pub_flux BOOLEAN NOT NULL DEFAULT FALSE,
+ CONSTRAINT cin_user_settings_pk PRIMARY KEY (user_id),
+ CONSTRAINT cin_user_settings_user_fk FOREIGN KEY (user_id)
+ REFERENCES solaruser.user_user (id) MATCH SIMPLE
+ ON UPDATE NO ACTION ON DELETE CASCADE
+);
+
+/**
+ * Cloud datum stream settings, to override cin_user_settings.
+ *
+ * @column user_id the ID of the account owner
+ * @column ds_id the ID of the datum stream associated with this configuration
+ * @column created the creation date
+ * @column modified the modification date
+ * @column pub_in a flag to publish datum streams to SolarIn
+ * @column pub_flux a flag to publish datum streams to SolarFlux
+ */
+CREATE TABLE solardin.cin_datum_stream_settings (
+ user_id BIGINT NOT NULL,
+ ds_id BIGINT NOT NULL,
+ created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ modified TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ pub_in BOOLEAN NOT NULL DEFAULT TRUE,
+ pub_flux BOOLEAN NOT NULL DEFAULT TRUE,
+ CONSTRAINT cin_datum_stream_settings_pk PRIMARY KEY (user_id, ds_id),
+ CONSTRAINT cin_datum_stream_settings_ds_fk FOREIGN KEY (user_id, ds_id)
+ REFERENCES solardin.cin_datum_stream (user_id, id) MATCH SIMPLE
+ ON UPDATE NO ACTION ON DELETE CASCADE
+);
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/DaoCloudDatumStreamPollService.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/DaoCloudDatumStreamPollService.java
index b718ec663..252b55c09 100644
--- a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/DaoCloudDatumStreamPollService.java
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/biz/impl/DaoCloudDatumStreamPollService.java
@@ -52,9 +52,12 @@
import net.solarnetwork.central.c2c.biz.CloudDatumStreamService;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamConfigurationDao;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamPollTaskDao;
+import net.solarnetwork.central.c2c.dao.CloudDatumStreamSettingsEntityDao;
+import net.solarnetwork.central.c2c.domain.BasicCloudDatumStreamSettings;
import net.solarnetwork.central.c2c.domain.BasicQueryFilter;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamConfiguration;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamPollTaskEntity;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettings;
import net.solarnetwork.central.c2c.domain.CloudIntegrationsUserEvents;
import net.solarnetwork.central.dao.SolarNodeOwnershipDao;
import net.solarnetwork.central.datum.domain.GeneralObjectDatum;
@@ -70,7 +73,7 @@
* DAO based implementation of {@link CloudDatumStreamPollService}.
*
* @author matt
- * @version 1.2
+ * @version 1.3
*/
public class DaoCloudDatumStreamPollService
implements CloudDatumStreamPollService, ServiceLifecycleObserver, CloudIntegrationsUserEvents {
@@ -78,6 +81,10 @@ public class DaoCloudDatumStreamPollService
/** The {@code shutdownMaxWait} property default value: 1 minute. */
public static final Duration DEFAULT_SHUTDOWN_MAX_WAIT = Duration.ofMinutes(1);
+ /** The {@code defaultDatumStreamSettings} default value. */
+ public static final CloudDatumStreamSettings DEFAULT_DATUM_STREAM_SETTINGS = new BasicCloudDatumStreamSettings(
+ true, false);
+
private final Logger log = LoggerFactory.getLogger(getClass());
private final Clock clock;
@@ -85,10 +92,12 @@ public class DaoCloudDatumStreamPollService
private final SolarNodeOwnershipDao nodeOwnershipDao;
private final CloudDatumStreamPollTaskDao taskDao;
private final CloudDatumStreamConfigurationDao datumStreamDao;
+ private final CloudDatumStreamSettingsEntityDao datumStreamSettingsDao;
private final DatumWriteOnlyDao datumDao;
private final ExecutorService executorService;
private final Function datumStreamServiceProvider;
private Duration shutdownMaxWait = DEFAULT_SHUTDOWN_MAX_WAIT;
+ private CloudDatumStreamSettings defaultDatumStreamSettings = DEFAULT_DATUM_STREAM_SETTINGS;
/**
* Constructor.
@@ -103,6 +112,8 @@ public class DaoCloudDatumStreamPollService
* the task DAO
* @param datumStreamDao
* the datum stream DAO
+ * @param datumStreamSettingsDao
+ * the datum stream settings DAO
* @param datumDao
* the datum DAO
* @param executor
@@ -116,7 +127,8 @@ public class DaoCloudDatumStreamPollService
*/
public DaoCloudDatumStreamPollService(Clock clock, UserEventAppenderBiz userEventAppenderBiz,
SolarNodeOwnershipDao nodeOwnershipDao, CloudDatumStreamPollTaskDao taskDao,
- CloudDatumStreamConfigurationDao datumStreamDao, DatumWriteOnlyDao datumDao,
+ CloudDatumStreamConfigurationDao datumStreamDao,
+ CloudDatumStreamSettingsEntityDao datumStreamSettingsDao, DatumWriteOnlyDao datumDao,
ExecutorService executor,
Function datumStreamServiceProvider) {
super();
@@ -125,6 +137,8 @@ public DaoCloudDatumStreamPollService(Clock clock, UserEventAppenderBiz userEven
this.nodeOwnershipDao = requireNonNullArgument(nodeOwnershipDao, "nodeOwnershipDao");
this.taskDao = requireNonNullArgument(taskDao, "taskDao");
this.datumStreamDao = requireNonNullArgument(datumStreamDao, "datumStreamDao");
+ this.datumStreamSettingsDao = requireNonNullArgument(datumStreamSettingsDao,
+ "datumStreamSettingsDao");
this.datumDao = requireNonNullArgument(datumDao, "datumDao");
this.executorService = requireNonNullArgument(executor, "executor");
this.datumStreamServiceProvider = requireNonNullArgument(datumStreamServiceProvider,
@@ -247,6 +261,9 @@ private CloudDatumStreamPollTaskEntity executeTask() throws Exception {
return taskInfo;
}
+ final CloudDatumStreamSettings datumStreamSettings = datumStreamSettingsDao.resolveSettings(
+ datumStream.getUserId(), datumStream.getConfigId(), defaultDatumStreamSettings);
+
final String datumStreamIdent = datumStream.getId().ident();
if ( !datumStream.isFullyConfigured() ) {
@@ -336,11 +353,17 @@ private CloudDatumStreamPollTaskEntity executeTask() throws Exception {
polledDatum.size());
for ( var datum : polledDatum ) {
if ( datum instanceof DatumEntity d ) {
- datumDao.store(d);
+ if ( datumStreamSettings.isPublishToSolarIn() ) {
+ datumDao.store(d);
+ }
} else if ( datum instanceof GeneralObjectDatum> d ) {
- datumDao.persist(d);
+ if ( datumStreamSettings.isPublishToSolarIn() ) {
+ datumDao.persist(d);
+ }
} else {
- datumDao.store(datum);
+ if ( datumStreamSettings.isPublishToSolarIn() ) {
+ datumDao.store(datum);
+ }
}
if ( lastDatumDate == null || lastDatumDate.isBefore(datum.getTimestamp()) ) {
lastDatumDate = datum.getTimestamp();
@@ -434,4 +457,29 @@ public final void setShutdownMaxWait(Duration shutdownMaxWait) {
this.shutdownMaxWait = (shutdownMaxWait != null ? shutdownMaxWait : DEFAULT_SHUTDOWN_MAX_WAIT);
}
+ /**
+ * Get the default datum stream settings.
+ *
+ * @return the settings, never {@literal null}
+ * @since 1.3
+ */
+ public final CloudDatumStreamSettings getDefaultDatumStreamSettings() {
+ return defaultDatumStreamSettings;
+ }
+
+ /**
+ * Set the default datum stream settings.
+ *
+ * @param defaultDatumStreamSettings
+ * the settings to set; if {@code null} then
+ * {@link #DEFAULT_DATUM_STREAM_SETTINGS} will be used
+ * @since 1.3
+ */
+ public final void setDefaultDatumStreamSettings(
+ CloudDatumStreamSettings defaultDatumStreamSettings) {
+ this.defaultDatumStreamSettings = (defaultDatumStreamSettings != null
+ ? defaultDatumStreamSettings
+ : DEFAULT_DATUM_STREAM_SETTINGS);
+ }
+
}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/config/CloudIntegrationsDaoConfig.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/config/CloudIntegrationsDaoConfig.java
index 57011d152..ccd51ada5 100644
--- a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/config/CloudIntegrationsDaoConfig.java
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/config/CloudIntegrationsDaoConfig.java
@@ -32,18 +32,22 @@
import net.solarnetwork.central.c2c.dao.CloudDatumStreamMappingConfigurationDao;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamPollTaskDao;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamPropertyConfigurationDao;
+import net.solarnetwork.central.c2c.dao.CloudDatumStreamSettingsEntityDao;
import net.solarnetwork.central.c2c.dao.CloudIntegrationConfigurationDao;
+import net.solarnetwork.central.c2c.dao.UserSettingsEntityDao;
import net.solarnetwork.central.c2c.dao.jdbc.JdbcCloudDatumStreamConfigurationDao;
import net.solarnetwork.central.c2c.dao.jdbc.JdbcCloudDatumStreamMappingConfigurationDao;
import net.solarnetwork.central.c2c.dao.jdbc.JdbcCloudDatumStreamPollTaskDao;
import net.solarnetwork.central.c2c.dao.jdbc.JdbcCloudDatumStreamPropertyConfigurationDao;
+import net.solarnetwork.central.c2c.dao.jdbc.JdbcCloudDatumStreamSettingsEntityDao;
import net.solarnetwork.central.c2c.dao.jdbc.JdbcCloudIntegrationConfigurationDao;
+import net.solarnetwork.central.c2c.dao.jdbc.JdbcUserSettingsEntityDao;
/**
* Cloud integrations DAO configuration.
*
* @author matt
- * @version 1.1
+ * @version 1.2
*/
@Configuration(proxyBeanMethods = false)
@Profile(CLOUD_INTEGRATIONS)
@@ -58,7 +62,7 @@ public class CloudIntegrationsDaoConfig {
* @return the DAO
*/
@Bean
- public CloudIntegrationConfigurationDao cloudIntegrationConfigurationConfigurationDao() {
+ public CloudIntegrationConfigurationDao cloudIntegrationConfigurationDao() {
return new JdbcCloudIntegrationConfigurationDao(jdbcOperations);
}
@@ -68,7 +72,7 @@ public CloudIntegrationConfigurationDao cloudIntegrationConfigurationConfigurati
* @return the DAO
*/
@Bean
- public CloudDatumStreamConfigurationDao cloudDatumStreamConfigurationConfigurationDao() {
+ public CloudDatumStreamConfigurationDao cloudDatumStreamConfigurationDao() {
return new JdbcCloudDatumStreamConfigurationDao(jdbcOperations);
}
@@ -78,7 +82,7 @@ public CloudDatumStreamConfigurationDao cloudDatumStreamConfigurationConfigurati
* @return the DAO
*/
@Bean
- public CloudDatumStreamMappingConfigurationDao cloudDatumStreamMappingConfigurationConfigurationDao() {
+ public CloudDatumStreamMappingConfigurationDao cloudDatumStreamMappingConfigurationDao() {
return new JdbcCloudDatumStreamMappingConfigurationDao(jdbcOperations);
}
@@ -88,7 +92,7 @@ public CloudDatumStreamMappingConfigurationDao cloudDatumStreamMappingConfigurat
* @return the DAO
*/
@Bean
- public CloudDatumStreamPropertyConfigurationDao cloudDatumStreamPropertyConfigurationConfigurationDao() {
+ public CloudDatumStreamPropertyConfigurationDao cloudDatumStreamPropertyConfigurationDao() {
return new JdbcCloudDatumStreamPropertyConfigurationDao(jdbcOperations);
}
@@ -102,4 +106,26 @@ public CloudDatumStreamPollTaskDao cloudDatumStreamPollTaskDaoDao() {
return new JdbcCloudDatumStreamPollTaskDao(jdbcOperations);
}
+ /**
+ * The user settings DAO.
+ *
+ * @return the DAO
+ * @since 1.2
+ */
+ @Bean
+ public UserSettingsEntityDao cloudIntegrationsUserSettingsEntityDao() {
+ return new JdbcUserSettingsEntityDao(jdbcOperations);
+ }
+
+ /**
+ * The cloud datum stream settings DAO.
+ *
+ * @return the DAO
+ * @since 1.2
+ */
+ @Bean
+ public CloudDatumStreamSettingsEntityDao cloudDatumStreamSettingsEntityDao() {
+ return new JdbcCloudDatumStreamSettingsEntityDao(jdbcOperations);
+ }
+
}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/BasicFilter.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/BasicFilter.java
index a0cab7b0d..0f3593561 100644
--- a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/BasicFilter.java
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/BasicFilter.java
@@ -39,11 +39,11 @@
* Basic implementation of cloud integration query filter.
*
* @author matt
- * @version 1.0
+ * @version 1.1
*/
public class BasicFilter extends BasicCoreCriteria
implements CloudIntegrationFilter, CloudDatumStreamFilter, CloudDatumStreamMappingFilter,
- CloudDatumStreamPropertyFilter, CloudDatumStreamPollTaskFilter {
+ CloudDatumStreamPropertyFilter, CloudDatumStreamPollTaskFilter, CloudDatumStreamSettingsFilter {
private Long[] integrationIds;
private Long[] datumStreamIds;
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsEntityDao.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsEntityDao.java
new file mode 100644
index 000000000..197934ce8
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsEntityDao.java
@@ -0,0 +1,57 @@
+/* ==================================================================
+ * CloudDatumStreamSettingsEntityDao.java - 28/10/2024 7:21:50 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao;
+
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamConfiguration;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettings;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettingsEntity;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+import net.solarnetwork.central.common.dao.GenericCompositeKey2Dao;
+import net.solarnetwork.central.domain.UserLongCompositePK;
+import net.solarnetwork.dao.FilterableDao;
+
+/**
+ * DAO API for {@link CloudDatumStreamSettingsEntity} entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public interface CloudDatumStreamSettingsEntityDao
+ extends GenericCompositeKey2Dao,
+ FilterableDao {
+
+ /**
+ * Get settings resolved using {@link UserSettingsEntity} defaults if a
+ * datum stream setting does not exist.
+ *
+ * @param userId
+ * the owner user ID
+ * @param datumStreamId
+ * the {@link CloudDatumStreamConfiguration} ID to resolve settings
+ * for
+ * @return the settings, or {@code defaultSettings} if no datum stream or
+ * user settings exist
+ */
+ CloudDatumStreamSettings resolveSettings(Long userId, Long datumStreamId,
+ CloudDatumStreamSettings defaultSettings);
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsFilter.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsFilter.java
new file mode 100644
index 000000000..515f40bf0
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/CloudDatumStreamSettingsFilter.java
@@ -0,0 +1,34 @@
+/* ==================================================================
+ * CloudDatumStreamSettingsFilter.java - 28/10/2024 8:11:33 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao;
+
+/**
+ * A filter for cloud datum stream settings entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public interface CloudDatumStreamSettingsFilter
+ extends CloudIntegrationsFilter, CloudDatumStreamCriteria {
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/UserSettingsEntityDao.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/UserSettingsEntityDao.java
new file mode 100644
index 000000000..7e61ca8db
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/UserSettingsEntityDao.java
@@ -0,0 +1,36 @@
+/* ==================================================================
+ * UserSettingsEntityDao.java - 28/10/2024 7:21:50 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao;
+
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+import net.solarnetwork.central.dao.GenericDao;
+
+/**
+ * DAO API for {@link UserSettingsEntity} entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public interface UserSettingsEntityDao extends GenericDao {
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/CloudDatumStreamSettingsEntityRowMapper.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/CloudDatumStreamSettingsEntityRowMapper.java
new file mode 100644
index 000000000..cbd22f77b
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/CloudDatumStreamSettingsEntityRowMapper.java
@@ -0,0 +1,90 @@
+/* ==================================================================
+ * CloudDatumStreamSettingsEntityRowMapper.java - 28/10/2024 7:54:26 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc;
+
+import static net.solarnetwork.central.common.dao.jdbc.sql.CommonJdbcUtils.getTimestampInstant;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.Instant;
+import org.springframework.jdbc.core.RowMapper;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettingsEntity;
+
+/**
+ * Row mapper for {@link CloudDatumStreamSettingsEntity} entities.
+ *
+ *
+ * The expected column order in the SQL results is:
+ *
+ *
+ *
+ * - user_id (BIGINT)
+ * - ds_id (BIGINT)
+ * - created (TIMESTAMP)
+ * - modified (TIMESTAMP)
+ * - pub_in (BOOLEAN)
+ * - pub_flux (BOOLEAN)
+ *
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class CloudDatumStreamSettingsEntityRowMapper
+ implements RowMapper {
+
+ /** A default instance. */
+ public static final RowMapper INSTANCE = new CloudDatumStreamSettingsEntityRowMapper();
+
+ private final int columnOffset;
+
+ /**
+ * Default constructor.
+ */
+ public CloudDatumStreamSettingsEntityRowMapper() {
+ this(0);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param columnOffset
+ * a column offset to apply
+ */
+ public CloudDatumStreamSettingsEntityRowMapper(int columnOffset) {
+ this.columnOffset = columnOffset;
+ }
+
+ @Override
+ public CloudDatumStreamSettingsEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+ int p = columnOffset;
+ Long userId = rs.getObject(++p, Long.class);
+ Long datumStreamId = rs.getObject(++p, Long.class);
+ Instant ts = getTimestampInstant(rs, ++p);
+ CloudDatumStreamSettingsEntity conf = new CloudDatumStreamSettingsEntity(userId, datumStreamId,
+ ts);
+ conf.setModified(getTimestampInstant(rs, ++p));
+ conf.setPublishToSolarIn(rs.getBoolean(++p));
+ conf.setPublishToSolarFlux(rs.getBoolean(++p));
+ return conf;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcCloudDatumStreamSettingsEntityDao.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcCloudDatumStreamSettingsEntityDao.java
new file mode 100644
index 000000000..1f7c3c95b
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcCloudDatumStreamSettingsEntityDao.java
@@ -0,0 +1,151 @@
+/* ==================================================================
+ * JdbcCloudDatumStreamSettingsEntityDao.java - 28/10/2024 10:06:28 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc;
+
+import static java.time.Instant.now;
+import static java.util.stream.StreamSupport.stream;
+import static net.solarnetwork.central.common.dao.jdbc.sql.CommonJdbcUtils.executeFilterQuery;
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.util.Collection;
+import java.util.List;
+import org.springframework.jdbc.core.JdbcOperations;
+import net.solarnetwork.central.c2c.dao.BasicFilter;
+import net.solarnetwork.central.c2c.dao.CloudDatumStreamSettingsEntityDao;
+import net.solarnetwork.central.c2c.dao.CloudDatumStreamSettingsFilter;
+import net.solarnetwork.central.c2c.dao.jdbc.sql.SelectCloudDatumStreamSettingsEntity;
+import net.solarnetwork.central.c2c.dao.jdbc.sql.UpsertCloudDatumStreamSettingsEntity;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettings;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettingsEntity;
+import net.solarnetwork.central.common.dao.jdbc.sql.DeleteForCompositeKey;
+import net.solarnetwork.central.domain.UserLongCompositePK;
+import net.solarnetwork.dao.FilterResults;
+import net.solarnetwork.domain.SortDescriptor;
+
+/**
+ * JDBC implementation of {@link CloudDatumStreamSettingsEntityDao}.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class JdbcCloudDatumStreamSettingsEntityDao implements CloudDatumStreamSettingsEntityDao {
+
+ private final JdbcOperations jdbcOps;
+
+ /**
+ * Constructor.
+ *
+ * @param jdbcOps
+ * the JDBC operations
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public JdbcCloudDatumStreamSettingsEntityDao(JdbcOperations jdbcOps) {
+ super();
+ this.jdbcOps = requireNonNullArgument(jdbcOps, "jdbcOps");
+ }
+
+ @Override
+ public Class extends CloudDatumStreamSettingsEntity> getObjectType() {
+ return CloudDatumStreamSettingsEntity.class;
+ }
+
+ @Override
+ public CloudDatumStreamSettingsEntity entityKey(UserLongCompositePK id) {
+ return new CloudDatumStreamSettingsEntity(id, now());
+ }
+
+ @Override
+ public UserLongCompositePK create(Long userId, CloudDatumStreamSettingsEntity entity) {
+ requireNonNullArgument(entity, "entity");
+ final var sql = new UpsertCloudDatumStreamSettingsEntity(userId, entity.getDatumStreamId(),
+ entity);
+ int count = jdbcOps.update(sql);
+ return (count > 0 ? new UserLongCompositePK(userId, entity.getDatumStreamId()) : null);
+ }
+
+ @Override
+ public Collection findAll(Long userId, List sorts) {
+ var filter = new BasicFilter();
+ filter.setUserId(requireNonNullArgument(userId, "userId"));
+ var results = findFiltered(filter, sorts, null, null);
+ return stream(results.spliterator(), false).toList();
+ }
+
+ @Override
+ public FilterResults findFiltered(
+ CloudDatumStreamSettingsFilter filter, List sorts, Integer offset,
+ Integer max) {
+ requireNonNullArgument(requireNonNullArgument(filter, "filter").getUserId(), "filter.userId");
+ var sql = new SelectCloudDatumStreamSettingsEntity(filter);
+ return executeFilterQuery(jdbcOps, filter, sql,
+ CloudDatumStreamSettingsEntityRowMapper.INSTANCE);
+ }
+
+ @Override
+ public UserLongCompositePK save(CloudDatumStreamSettingsEntity entity) {
+ requireNonNullArgument(entity, "entity");
+ return create(entity.getUserId(), entity);
+ }
+
+ @Override
+ public CloudDatumStreamSettingsEntity get(UserLongCompositePK id) {
+ var filter = new BasicFilter();
+ filter.setUserId(
+ requireNonNullArgument(requireNonNullArgument(id, "id").getUserId(), "id.userId"));
+ filter.setDatumStreamId(requireNonNullArgument(id.getEntityId(), "id.entityId"));
+ var sql = new SelectCloudDatumStreamSettingsEntity(filter);
+ var results = executeFilterQuery(jdbcOps, filter, sql,
+ CloudDatumStreamSettingsEntityRowMapper.INSTANCE);
+ return stream(results.spliterator(), false).findFirst().orElse(null);
+ }
+
+ @Override
+ public Collection getAll(List sorts) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static final String TABLE_NAME = "solardin.cin_datum_stream_settings";
+ private static final String ID_COLUMN_NAME = "ds_id";
+ private static final String[] PK_COLUMN_NAMES = new String[] { "user_id", ID_COLUMN_NAME };
+
+ @Override
+ public void delete(CloudDatumStreamSettingsEntity entity) {
+ DeleteForCompositeKey sql = new DeleteForCompositeKey(
+ requireNonNullArgument(entity, "entity").getId(), TABLE_NAME, PK_COLUMN_NAMES);
+ jdbcOps.update(sql);
+ }
+
+ @Override
+ public CloudDatumStreamSettings resolveSettings(Long userId, Long datumStreamId,
+ CloudDatumStreamSettings defaultSettings) {
+ var filter = new BasicFilter();
+ filter.setUserId(requireNonNullArgument(userId, "userId"));
+ filter.setDatumStreamId(requireNonNullArgument(datumStreamId, "datumStreamId"));
+ var sql = new SelectCloudDatumStreamSettingsEntity(filter, true);
+ var results = executeFilterQuery(jdbcOps, filter, sql,
+ CloudDatumStreamSettingsEntityRowMapper.INSTANCE);
+ return stream(results.spliterator(), false).findFirst().map(CloudDatumStreamSettings.class::cast)
+ .orElse(defaultSettings);
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcUserSettingsEntityDao.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcUserSettingsEntityDao.java
new file mode 100644
index 000000000..c3399fef3
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/JdbcUserSettingsEntityDao.java
@@ -0,0 +1,91 @@
+/* ==================================================================
+ * JdbcUserSettingsEntityDao.java - 28/10/2024 7:34:16 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc;
+
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.util.List;
+import org.springframework.jdbc.core.JdbcOperations;
+import net.solarnetwork.central.c2c.dao.UserSettingsEntityDao;
+import net.solarnetwork.central.c2c.dao.jdbc.sql.DeleteUserSettingsEntity;
+import net.solarnetwork.central.c2c.dao.jdbc.sql.SelectUserSettingsEntity;
+import net.solarnetwork.central.c2c.dao.jdbc.sql.UpsertUserSettingsEntity;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+import net.solarnetwork.domain.SortDescriptor;
+
+/**
+ * JDBC implementation of {@link UserSettingsEntityDao}.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class JdbcUserSettingsEntityDao implements UserSettingsEntityDao {
+
+ private final JdbcOperations jdbcOps;
+
+ /**
+ * Constructor.
+ *
+ * @param jdbcOps
+ * the JDBC operations
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public JdbcUserSettingsEntityDao(JdbcOperations jdbcOps) {
+ super();
+ this.jdbcOps = requireNonNullArgument(jdbcOps, "jdbcOps");
+ }
+
+ @Override
+ public Class extends UserSettingsEntity> getObjectType() {
+ return UserSettingsEntity.class;
+ }
+
+ @Override
+ public Long store(UserSettingsEntity entity) {
+ Long userId = requireNonNullArgument(requireNonNullArgument(entity, "entity").getUserId(),
+ "entity.userId");
+ final var sql = new UpsertUserSettingsEntity(userId, entity);
+ int count = jdbcOps.update(sql);
+ return (count > 0 ? userId : null);
+ }
+
+ @Override
+ public UserSettingsEntity get(Long id) {
+ var sql = new SelectUserSettingsEntity(id);
+ List results = jdbcOps.query(sql, UserSettingsEntityRowMapper.INSTANCE);
+ return (results != null && !results.isEmpty() ? results.getFirst() : null);
+ }
+
+ @Override
+ public List getAll(List sortDescriptors) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void delete(UserSettingsEntity entity) {
+ var sql = new DeleteUserSettingsEntity(requireNonNullArgument(
+ requireNonNullArgument(entity, "entity").getUserId(), "entity.userId"));
+ jdbcOps.update(sql);
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/UserSettingsEntityRowMapper.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/UserSettingsEntityRowMapper.java
new file mode 100644
index 000000000..81d126d68
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/UserSettingsEntityRowMapper.java
@@ -0,0 +1,86 @@
+/* ==================================================================
+ * UserSettingsEntityRowMapper.java - 28/10/2024 7:54:26 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc;
+
+import static net.solarnetwork.central.common.dao.jdbc.sql.CommonJdbcUtils.getTimestampInstant;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.time.Instant;
+import org.springframework.jdbc.core.RowMapper;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+
+/**
+ * Row mapper for {@link UserSettingsEntity} entities.
+ *
+ *
+ * The expected column order in the SQL results is:
+ *
+ *
+ *
+ * - user_id (BIGINT)
+ * - created (TIMESTAMP)
+ * - modified (TIMESTAMP)
+ * - pub_in (BOOLEAN)
+ * - pub_flux (BOOLEAN)
+ *
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class UserSettingsEntityRowMapper implements RowMapper {
+
+ /** A default instance. */
+ public static final RowMapper INSTANCE = new UserSettingsEntityRowMapper();
+
+ private final int columnOffset;
+
+ /**
+ * Default constructor.
+ */
+ public UserSettingsEntityRowMapper() {
+ this(0);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param columnOffset
+ * a column offset to apply
+ */
+ public UserSettingsEntityRowMapper(int columnOffset) {
+ this.columnOffset = columnOffset;
+ }
+
+ @Override
+ public UserSettingsEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
+ int p = columnOffset;
+ Long userId = rs.getObject(++p, Long.class);
+ Instant ts = getTimestampInstant(rs, ++p);
+ UserSettingsEntity conf = new UserSettingsEntity(userId, ts);
+ conf.setModified(getTimestampInstant(rs, ++p));
+ conf.setPublishToSolarIn(rs.getBoolean(++p));
+ conf.setPublishToSolarFlux(rs.getBoolean(++p));
+ return conf;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/DeleteUserSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/DeleteUserSettingsEntity.java
new file mode 100644
index 000000000..6fb48d677
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/DeleteUserSettingsEntity.java
@@ -0,0 +1,71 @@
+/* ==================================================================
+ * SelectUserSettingsEntity.java - 28/10/2024 7:49:22 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc.sql;
+
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.SqlProvider;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+
+/**
+ * Support for DELETE for {@link UserSettingsEntity} entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class DeleteUserSettingsEntity implements PreparedStatementCreator, SqlProvider {
+
+ private static final String SQL = """
+ DELETE FROM solardin.cin_user_settings
+ WHERE user_id = ?
+ """;
+
+ private final Long userId;
+
+ /**
+ * Constructor.
+ *
+ * @param filter
+ * the filter
+ */
+ public DeleteUserSettingsEntity(Long userId) {
+ super();
+ this.userId = requireNonNullArgument(userId, "userId");
+ }
+
+ @Override
+ public String getSql() {
+ return SQL;
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+ PreparedStatement stmt = con.prepareStatement(getSql());
+ stmt.setObject(1, userId);
+ return stmt;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectCloudDatumStreamSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectCloudDatumStreamSettingsEntity.java
new file mode 100644
index 000000000..2d11d07c7
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectCloudDatumStreamSettingsEntity.java
@@ -0,0 +1,245 @@
+/* ==================================================================
+ * SelectCloudIntegrationConfiguration.java - 2/10/2024 8:46:14 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc.sql;
+
+import static net.solarnetwork.central.common.dao.jdbc.sql.CommonSqlUtils.prepareOptimizedArrayParameter;
+import static net.solarnetwork.central.common.dao.jdbc.sql.CommonSqlUtils.whereOptimizedArrayContains;
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.SqlProvider;
+import net.solarnetwork.central.c2c.dao.CloudDatumStreamSettingsFilter;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettingsEntity;
+import net.solarnetwork.central.common.dao.jdbc.CountPreparedStatementCreatorProvider;
+import net.solarnetwork.central.common.dao.jdbc.sql.CommonSqlUtils;
+
+/**
+ * Support for SELECT for {@link CloudDatumStreamSettingsEntity} entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class SelectCloudDatumStreamSettingsEntity
+ implements PreparedStatementCreator, SqlProvider, CountPreparedStatementCreatorProvider {
+
+ /** The {@code fetchSize} property default value. */
+ public static final int DEFAULT_FETCH_SIZE = 1000;
+
+ private final CloudDatumStreamSettingsFilter filter;
+ private final boolean resolveUserSettings;
+ private final int fetchSize;
+
+ /**
+ * Constructor.
+ *
+ *
+ * The {@link #DEFAULT_FETCH_SIZE} will be used and
+ * {@code resolveUserSettings} will be {@code false}.
+ *
+ *
+ * @param filter
+ * the filter
+ * @throws IllegalArgumentException
+ * if any argument is {@code null}
+ */
+ public SelectCloudDatumStreamSettingsEntity(CloudDatumStreamSettingsFilter filter) {
+ this(filter, false, DEFAULT_FETCH_SIZE);
+ }
+
+ /**
+ * Constructor.
+ *
+ *
+ * The {@link #DEFAULT_FETCH_SIZE} will be used.
+ *
+ *
+ * @param filter
+ * the filter
+ * @param resolveUserSettings
+ * {@code true} to resolve user settings as default values; only
+ * honored if the {@code filter} also provides a user ID and datum
+ * stream ID
+ * @throws IllegalArgumentException
+ * if any argument is {@code null}
+ */
+ public SelectCloudDatumStreamSettingsEntity(CloudDatumStreamSettingsFilter filter,
+ boolean resolveUserSettings) {
+ this(filter, resolveUserSettings, DEFAULT_FETCH_SIZE);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param filter
+ * the filter
+ * @param resolveUserSettings
+ * {@code true} to resolve user settings as default values; only
+ * honored if the {@code filter} also provides a user ID and datum
+ * stream ID
+ * @param fetchSize
+ * the fetch size
+ * @throws IllegalArgumentException
+ * if any argument is {@code null}
+ */
+ public SelectCloudDatumStreamSettingsEntity(CloudDatumStreamSettingsFilter filter,
+ boolean resolveUserSettings, int fetchSize) {
+ super();
+ this.filter = requireNonNullArgument(filter, "filter");
+ this.resolveUserSettings = resolveUserSettings && filter.hasUserCriteria()
+ && filter.hasDatumStreamCriteria();
+ this.fetchSize = fetchSize;
+ }
+
+ @Override
+ public String getSql() {
+ StringBuilder buf = new StringBuilder();
+ sqlCore(buf);
+ sqlWhere(buf);
+ if ( !resolveUserSettings ) {
+ sqlOrderBy(buf);
+ }
+ CommonSqlUtils.limitOffset(filter, buf);
+ return buf.toString();
+ }
+
+ private void sqlCore(StringBuilder buf) {
+ if ( resolveUserSettings ) {
+ buf.append("""
+ WITH cdss AS (
+ SELECT user_id
+ , ds_id
+ , created
+ , modified
+ , pub_in
+ , pub_flux
+ FROM solardin.cin_datum_stream_settings
+ WHERE user_id = ?
+ AND ds_id = ?
+
+ UNION ALL
+
+ SELECT user_id
+ , x'8000000000000000'::BIGINT AS ds_id
+ , created
+ , modified
+ , pub_in
+ , pub_flux
+ FROM solardin.cin_user_settings
+ WHERE user_id = ?
+ )
+ SELECT user_id
+ , ds_id
+ , created
+ , modified
+ , pub_in
+ , pub_flux
+ FROM cdss
+ LIMIT 1
+ """);
+ } else {
+ buf.append("""
+ SELECT cdss.user_id
+ , cdss.ds_id
+ , cdss.created
+ , cdss.modified
+ , cdss.pub_in
+ , cdss.pub_flux
+ FROM solardin.cin_datum_stream_settings cdss
+ """);
+ }
+ }
+
+ private void sqlWhere(StringBuilder buf) {
+ if ( resolveUserSettings ) {
+ return;
+ }
+ StringBuilder where = new StringBuilder();
+ int idx = 0;
+ if ( filter.hasUserCriteria() ) {
+ idx += whereOptimizedArrayContains(filter.getUserIds(), "cdss.user_id", where);
+ }
+ if ( filter.hasDatumStreamCriteria() ) {
+ idx += whereOptimizedArrayContains(filter.getDatumStreamIds(), "cdss.ds_id", where);
+ }
+ if ( idx > 0 ) {
+ buf.append("WHERE").append(where.substring(4));
+ }
+ }
+
+ private void sqlOrderBy(StringBuilder buf) {
+ buf.append("ORDER BY cdss.user_id, cdss.ds_id");
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+ PreparedStatement stmt = con.prepareStatement(getSql(), ResultSet.TYPE_FORWARD_ONLY,
+ ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
+ int p = prepareCore(con, stmt, 0);
+ CommonSqlUtils.prepareLimitOffset(filter, con, stmt, p);
+ if ( fetchSize > 0 ) {
+ stmt.setFetchSize(fetchSize);
+ }
+ return stmt;
+ }
+
+ private int prepareCore(Connection con, PreparedStatement stmt, int p) throws SQLException {
+ if ( filter.hasUserCriteria() ) {
+ p = prepareOptimizedArrayParameter(con, stmt, p, filter.getUserIds());
+ }
+ if ( filter.hasDatumStreamCriteria() ) {
+ p = prepareOptimizedArrayParameter(con, stmt, p, filter.getDatumStreamIds());
+ }
+ if ( resolveUserSettings ) {
+ p = prepareOptimizedArrayParameter(con, stmt, p, filter.getUserIds());
+ }
+ return p;
+ }
+
+ @Override
+ public PreparedStatementCreator countPreparedStatementCreator() {
+ return new CountPreparedStatementCreator();
+ }
+
+ private final class CountPreparedStatementCreator implements PreparedStatementCreator, SqlProvider {
+
+ @Override
+ public String getSql() {
+ StringBuilder buf = new StringBuilder();
+ sqlCore(buf);
+ sqlWhere(buf);
+ return CommonSqlUtils.wrappedCountQuery(buf.toString());
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+ PreparedStatement stmt = con.prepareStatement(getSql());
+ prepareCore(con, stmt, 0);
+ return stmt;
+ }
+
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectUserSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectUserSettingsEntity.java
new file mode 100644
index 000000000..e287e7f48
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/SelectUserSettingsEntity.java
@@ -0,0 +1,74 @@
+/* ==================================================================
+ * SelectUserSettingsEntity.java - 28/10/2024 7:49:22 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc.sql;
+
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.SqlProvider;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+
+/**
+ * Support for SELECT for {@link UserSettingsEntity} entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class SelectUserSettingsEntity implements PreparedStatementCreator, SqlProvider {
+
+ private static final String SQL = """
+ SELECT user_id,created,modified,pub_in,pub_flux
+ FROM solardin.cin_user_settings
+ WHERE user_id = ?
+ """;
+
+ private final Long userId;
+
+ /**
+ * Constructor.
+ *
+ * @param filter
+ * the filter
+ */
+ public SelectUserSettingsEntity(Long userId) {
+ super();
+ this.userId = requireNonNullArgument(userId, "userId");
+ }
+
+ @Override
+ public String getSql() {
+ return SQL;
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+ PreparedStatement stmt = con.prepareStatement(getSql(), ResultSet.TYPE_FORWARD_ONLY,
+ ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT);
+ stmt.setObject(1, userId);
+ return stmt;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertCloudDatumStreamSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertCloudDatumStreamSettingsEntity.java
new file mode 100644
index 000000000..de6d2fb6b
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertCloudDatumStreamSettingsEntity.java
@@ -0,0 +1,100 @@
+/* ==================================================================
+ * UpsertCloudDatumStreamSettingsEntity.java - 28/10/2024 7:38:26 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc.sql;
+
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.Instant;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.SqlProvider;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettingsEntity;
+
+/**
+ * Support for INSERT ... ON CONFLICT {@link CloudDatumStreamSettingsEntity}
+ * entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class UpsertCloudDatumStreamSettingsEntity implements PreparedStatementCreator, SqlProvider {
+
+ private static final String SQL = """
+ INSERT INTO solardin.cin_datum_stream_settings (
+ user_id,ds_id,created,modified,pub_in,pub_flux
+ )
+ VALUES (?,?,?,?,?,?)
+ ON CONFLICT (user_id, ds_id) DO UPDATE
+ SET modified = COALESCE(EXCLUDED.modified, CURRENT_TIMESTAMP)
+ , pub_in = EXCLUDED.pub_in
+ , pub_flux = EXCLUDED.pub_flux
+ """;
+
+ private final Long userId;
+ private final Long datumStreamId;
+ private final CloudDatumStreamSettingsEntity entity;
+
+ /**
+ * Constructor.
+ *
+ * @param userId
+ * the user ID
+ * @param datumStreamId
+ * the datum stream ID
+ * @param entity
+ * the entity
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public UpsertCloudDatumStreamSettingsEntity(Long userId, Long datumStreamId,
+ CloudDatumStreamSettingsEntity entity) {
+ super();
+ this.userId = requireNonNullArgument(userId, "userId");
+ this.datumStreamId = requireNonNullArgument(datumStreamId, "datumStreamId");
+ this.entity = requireNonNullArgument(entity, "entity");
+ }
+
+ @Override
+ public String getSql() {
+ return SQL;
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+ Timestamp ts = Timestamp.from(entity.getCreated() != null ? entity.getCreated() : Instant.now());
+ Timestamp mod = entity.getModified() != null ? Timestamp.from(entity.getModified()) : ts;
+ PreparedStatement stmt = con.prepareStatement(getSql(), Statement.NO_GENERATED_KEYS);
+ int p = 0;
+ stmt.setObject(++p, userId);
+ stmt.setObject(++p, datumStreamId);
+ stmt.setTimestamp(++p, ts);
+ stmt.setTimestamp(++p, mod);
+ stmt.setBoolean(++p, entity.isPublishToSolarIn());
+ stmt.setBoolean(++p, entity.isPublishToSolarFlux());
+ return stmt;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertUserSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertUserSettingsEntity.java
new file mode 100644
index 000000000..563a1c02c
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/dao/jdbc/sql/UpsertUserSettingsEntity.java
@@ -0,0 +1,93 @@
+/* ==================================================================
+ * UpsertUserSettingsEntity.java - 28/10/2024 7:38:26 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.dao.jdbc.sql;
+
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.Instant;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.SqlProvider;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
+
+/**
+ * Support for INSERT ... ON CONFLICT {@link UserSettingsEntity} entities.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public class UpsertUserSettingsEntity implements PreparedStatementCreator, SqlProvider {
+
+ private static final String SQL = """
+ INSERT INTO solardin.cin_user_settings (
+ user_id,created,modified,pub_in,pub_flux
+ )
+ VALUES (?,?,?,?,?)
+ ON CONFLICT (user_id) DO UPDATE
+ SET modified = COALESCE(EXCLUDED.modified, CURRENT_TIMESTAMP)
+ , pub_in = EXCLUDED.pub_in
+ , pub_flux = EXCLUDED.pub_flux
+ """;
+
+ private final Long userId;
+ private final UserSettingsEntity entity;
+
+ /**
+ * Constructor.
+ *
+ * @param userId
+ * the user ID
+ * @param entity
+ * the entity
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public UpsertUserSettingsEntity(Long userId, UserSettingsEntity entity) {
+ super();
+ this.userId = requireNonNullArgument(userId, "userId");
+ this.entity = requireNonNullArgument(entity, "entity");
+ }
+
+ @Override
+ public String getSql() {
+ return SQL;
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
+ Timestamp ts = Timestamp.from(entity.getCreated() != null ? entity.getCreated() : Instant.now());
+ Timestamp mod = entity.getModified() != null ? Timestamp.from(entity.getModified()) : ts;
+ PreparedStatement stmt = con.prepareStatement(getSql(), Statement.NO_GENERATED_KEYS);
+ int p = 0;
+ stmt.setObject(++p, userId);
+ stmt.setTimestamp(++p, ts);
+ stmt.setTimestamp(++p, mod);
+ stmt.setBoolean(++p, entity.isPublishToSolarIn());
+ stmt.setBoolean(++p, entity.isPublishToSolarFlux());
+ return stmt;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/BasicCloudDatumStreamSettings.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/BasicCloudDatumStreamSettings.java
new file mode 100644
index 000000000..322fa61ec
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/BasicCloudDatumStreamSettings.java
@@ -0,0 +1,48 @@
+/* ==================================================================
+ * BasicCloudDatumStreamSettings.java - 28/10/2024 2:26:59 pm
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.domain;
+
+/**
+ * Basic implementation of {@link CloudDatumStreamSettings}.
+ *
+ * @param publishToSolarIn
+ * the SolarIn publish mode
+ * @param publishToSolarFlux
+ * the SolarFlux publish mode
+ * @author matt
+ * @version 1.0
+ */
+public record BasicCloudDatumStreamSettings(boolean publishToSolarIn, boolean publishToSolarFlux)
+ implements CloudDatumStreamSettings {
+
+ @Override
+ public boolean isPublishToSolarIn() {
+ return publishToSolarIn;
+ }
+
+ @Override
+ public boolean isPublishToSolarFlux() {
+ return publishToSolarFlux;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettings.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettings.java
new file mode 100644
index 000000000..521f8503a
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettings.java
@@ -0,0 +1,49 @@
+/* ==================================================================
+ * CloudDatumStreamSettings.java - 28/10/2024 7:28:37 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.domain;
+
+/**
+ * Cloud datum stream configurable settings.
+ *
+ * @author matt
+ * @version 1.0
+ */
+public interface CloudDatumStreamSettings {
+
+ /**
+ * Get the "publish to SolarIn" toggle.
+ *
+ * @return {@literal true} if data should be published to SolarIn; defaults
+ * to {@literal true}
+ */
+ boolean isPublishToSolarIn();
+
+ /**
+ * Get the "publish to SolarFlux" toggle.
+ *
+ * @return {@literal true} if data should be published to SolarFlux;
+ * defaults to {@literal true}
+ */
+ boolean isPublishToSolarFlux();
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettingsEntity.java
new file mode 100644
index 000000000..03684c43e
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/CloudDatumStreamSettingsEntity.java
@@ -0,0 +1,169 @@
+/* ==================================================================
+ * CloudDatumStreamSettingsEntity.java - 28/10/2024 7:15:32 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.domain;
+
+import java.time.Instant;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import net.solarnetwork.central.dao.BaseUserModifiableEntity;
+import net.solarnetwork.central.domain.UserLongCompositePK;
+
+/**
+ * Cloud datum stream settings, to override {@link UserSettingsEntity}.
+ *
+ * @author matt
+ * @version 1.0
+ */
+@JsonIgnoreProperties({ "id", "enabled" })
+@JsonPropertyOrder({ "userId", "datumStreamId", "created", "modified", "publishToSolarIn",
+ "publishToSolarFlux" })
+public class CloudDatumStreamSettingsEntity
+ extends BaseUserModifiableEntity implements
+ CloudIntegrationsConfigurationEntity,
+ CloudDatumStreamSettings {
+
+ private static final long serialVersionUID = -5768166630955664067L;
+
+ private boolean publishToSolarIn = true;
+ private boolean publishToSolarFlux = false;
+
+ /**
+ * Constructor.
+ *
+ * @param userId
+ * the user ID
+ * @param dataSourceId
+ * the data source ID
+ * @param created
+ * the creation date
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public CloudDatumStreamSettingsEntity(UserLongCompositePK id, Instant created) {
+ super(id, created);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param userId
+ * the user ID
+ * @param dataSourceId
+ * the data source ID
+ * @param created
+ * the creation date
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public CloudDatumStreamSettingsEntity(Long userId, Long dataSourceId, Instant created) {
+ this(new UserLongCompositePK(userId, dataSourceId), created);
+ }
+
+ @Override
+ public boolean isFullyConfigured() {
+ return true;
+ }
+
+ @Override
+ public CloudDatumStreamSettingsEntity copyWithId(UserLongCompositePK id) {
+ var copy = new CloudDatumStreamSettingsEntity(id, getCreated());
+ copyTo(copy);
+ return copy;
+ }
+
+ @Override
+ public void copyTo(CloudDatumStreamSettingsEntity entity) {
+ super.copyTo(entity);
+ entity.setPublishToSolarIn(publishToSolarIn);
+ entity.setPublishToSolarFlux(publishToSolarFlux);
+ }
+
+ @Override
+ public boolean isSameAs(CloudDatumStreamSettingsEntity other) {
+ boolean result = super.isSameAs(other);
+ if ( !result ) {
+ return false;
+ }
+ // @formatter:off
+ return publishToSolarIn == other.publishToSolarIn
+ && publishToSolarFlux == other.publishToSolarFlux
+ ;
+ // @formatter:on
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(64);
+ builder.append("CloudDatumStreamSettingsEntity{userId=");
+ builder.append(getUserId());
+ builder.append(", datumStreamId=");
+ builder.append(getDatumStreamId());
+ builder.append(", publishToSolarIn=");
+ builder.append(publishToSolarIn);
+ builder.append(", publishToSolarFlux=");
+ builder.append(publishToSolarFlux);
+ builder.append("}");
+ return builder.toString();
+ }
+
+ /**
+ * Get the cloud datum stream ID.
+ *
+ * @return the cloud datum stream ID
+ */
+ public Long getDatumStreamId() {
+ UserLongCompositePK id = getId();
+ return (id != null ? id.getEntityId() : null);
+ }
+
+ @Override
+ public boolean isPublishToSolarIn() {
+ return publishToSolarIn;
+ }
+
+ /**
+ * Set the "publish to SolarIn" toggle.
+ *
+ * @param publishToSolarIn
+ * {@literal true} if data should be published to SolarIn
+ */
+ public void setPublishToSolarIn(boolean publishToSolarIn) {
+ this.publishToSolarIn = publishToSolarIn;
+ }
+
+ @Override
+ public boolean isPublishToSolarFlux() {
+ return publishToSolarFlux;
+ }
+
+ /**
+ * Set the "publish to SolarFlux" toggle.
+ *
+ * @param publishToSolarFlux
+ * {@literal true} if data should be published to SolarFlux
+ */
+ public void setPublishToSolarFlux(boolean publishToSolarFlux) {
+ this.publishToSolarFlux = publishToSolarFlux;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/UserSettingsEntity.java b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/UserSettingsEntity.java
new file mode 100644
index 000000000..709bc02d1
--- /dev/null
+++ b/solarnet/cloud-integrations/src/main/java/net/solarnetwork/central/c2c/domain/UserSettingsEntity.java
@@ -0,0 +1,196 @@
+/* ==================================================================
+ * UserSettingsEntity.java - 28/10/2024 6:59:38 am
+ *
+ * Copyright 2024 SolarNetwork.net Dev Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ * ==================================================================
+ */
+
+package net.solarnetwork.central.c2c.domain;
+
+import static net.solarnetwork.util.ObjectUtils.requireNonNullArgument;
+import java.io.Serializable;
+import java.time.Instant;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import net.solarnetwork.central.dao.UserRelatedEntity;
+import net.solarnetwork.dao.BasicLongEntity;
+import net.solarnetwork.domain.CopyingIdentity;
+import net.solarnetwork.domain.Differentiable;
+
+/**
+ * Account-wide cloud integrations settings.
+ *
+ *
+ * The {@link #getId()} value represents the SolarNet user ID.
+ *
+ *
+ * @author matt
+ * @version 1.0
+ */
+@JsonIgnoreProperties({ "id" })
+@JsonPropertyOrder({ "userId", "created", "modified", "publishToSolarIn", "publishToSolarFlux" })
+public class UserSettingsEntity extends BasicLongEntity
+ implements Differentiable, UserRelatedEntity,
+ CopyingIdentity, Serializable, Cloneable, CloudDatumStreamSettings {
+
+ private static final long serialVersionUID = 2463852724878062639L;
+
+ private Instant modified;
+ private boolean publishToSolarIn = true;
+ private boolean publishToSolarFlux = false;
+
+ /**
+ * Constructor.
+ *
+ * @param userId
+ * the user ID
+ * @param created
+ * the creation date
+ * @throws IllegalArgumentException
+ * if any argument is {@literal null}
+ */
+ public UserSettingsEntity(Long userId, Instant created) {
+ super(userId, created);
+ }
+
+ @Override
+ public UserSettingsEntity copyWithId(Long id) {
+ var copy = new UserSettingsEntity(requireNonNullArgument(id, "id"), getCreated());
+ copyTo(copy);
+ return copy;
+ }
+
+ @Override
+ public void copyTo(UserSettingsEntity entity) {
+ entity.setModified(modified);
+ entity.setPublishToSolarIn(publishToSolarIn);
+ entity.setPublishToSolarFlux(publishToSolarFlux);
+ }
+
+ @Override
+ public boolean differsFrom(UserSettingsEntity other) {
+ return !isSameAs(other);
+ }
+
+ /**
+ * Test if the properties of another entity are the same as in this
+ * instance.
+ *
+ *
+ * The {@code id} and {@code created} properties are not compared by this
+ * method.
+ *
+ *
+ * @param other
+ * the other entity to compare to
+ * @return {@literal true} if the properties of this instance are equal to
+ * the other
+ */
+ public boolean isSameAs(UserSettingsEntity other) {
+ if ( other == null ) {
+ return false;
+ }
+ // @formatter:off
+ return publishToSolarIn == other.publishToSolarIn
+ && publishToSolarFlux == other.publishToSolarFlux
+ ;
+ // @formatter:on
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CloudDatumStream{userId=");
+ builder.append(getUserId());
+ builder.append(", publishToSolarIn=");
+ builder.append(publishToSolarIn);
+ builder.append(", publishToSolarFlux=");
+ builder.append(publishToSolarFlux);
+ builder.append("}");
+ return builder.toString();
+ }
+
+ @Override
+ public UserSettingsEntity clone() {
+ return (UserSettingsEntity) super.clone();
+ }
+
+ /**
+ * Get the user ID.
+ *
+ *
+ * This is an alias for {@link #getId()}.
+ *
+ *
+ * @return the user ID
+ */
+ @Override
+ public Long getUserId() {
+ return getId();
+ }
+
+ /**
+ * Get the modification date.
+ *
+ * @return the modified date
+ */
+ public Instant getModified() {
+ return modified;
+ }
+
+ /**
+ * Set the modification date.
+ *
+ * @param modified
+ * the modified date to set
+ */
+ public void setModified(Instant modified) {
+ this.modified = modified;
+ }
+
+ @Override
+ public boolean isPublishToSolarIn() {
+ return publishToSolarIn;
+ }
+
+ /**
+ * Set the "publish to SolarIn" toggle.
+ *
+ * @param publishToSolarIn
+ * {@literal true} if data should be published to SolarIn
+ */
+ public void setPublishToSolarIn(boolean publishToSolarIn) {
+ this.publishToSolarIn = publishToSolarIn;
+ }
+
+ @Override
+ public boolean isPublishToSolarFlux() {
+ return publishToSolarFlux;
+ }
+
+ /**
+ * Set the "publish to SolarFlux" toggle.
+ *
+ * @param publishToSolarFlux
+ * {@literal true} if data should be published to SolarFlux
+ */
+ public void setPublishToSolarFlux(boolean publishToSolarFlux) {
+ this.publishToSolarFlux = publishToSolarFlux;
+ }
+
+}
diff --git a/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/biz/impl/test/DaoCloudDatumStreamPollServiceTests.java b/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/biz/impl/test/DaoCloudDatumStreamPollServiceTests.java
index 345a1a6fc..1e932d806 100644
--- a/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/biz/impl/test/DaoCloudDatumStreamPollServiceTests.java
+++ b/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/biz/impl/test/DaoCloudDatumStreamPollServiceTests.java
@@ -24,6 +24,7 @@
import static java.time.Instant.now;
import static java.time.ZoneOffset.UTC;
+import static net.solarnetwork.central.c2c.biz.impl.DaoCloudDatumStreamPollService.DEFAULT_DATUM_STREAM_SETTINGS;
import static net.solarnetwork.central.domain.BasicClaimableJobState.Claimed;
import static net.solarnetwork.central.domain.BasicClaimableJobState.Completed;
import static net.solarnetwork.central.domain.BasicClaimableJobState.Executing;
@@ -65,6 +66,7 @@
import net.solarnetwork.central.c2c.biz.impl.DaoCloudDatumStreamPollService;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamConfigurationDao;
import net.solarnetwork.central.c2c.dao.CloudDatumStreamPollTaskDao;
+import net.solarnetwork.central.c2c.dao.CloudDatumStreamSettingsEntityDao;
import net.solarnetwork.central.c2c.domain.BasicCloudDatumStreamQueryResult;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamConfiguration;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamPollTaskEntity;
@@ -83,7 +85,7 @@
* Test cases for the {@link DaoCloudDatumStreamPollService} class.
*
* @author matt
- * @version 1.1
+ * @version 1.2
*/
@SuppressWarnings("static-access")
@ExtendWith(MockitoExtension.class)
@@ -106,6 +108,9 @@ public class DaoCloudDatumStreamPollServiceTests {
@Mock
private CloudDatumStreamConfigurationDao datumStreamDao;
+ @Mock
+ private CloudDatumStreamSettingsEntityDao datumStreamSettingsDao;
+
@Mock
private DatumWriteOnlyDao datumDao;
@@ -134,7 +139,8 @@ public void setup() {
var datumStreamServices = Map.of(TEST_DATUM_STREAM_SERVICE_IDENTIFIER, datumStreamService);
service = new DaoCloudDatumStreamPollService(clock, userEventAppenderBiz, nodeOwnershipDao,
- taskDao, datumStreamDao, datumDao, executor, datumStreamServices::get);
+ taskDao, datumStreamDao, datumStreamSettingsDao, datumDao, executor,
+ datumStreamServices::get);
}
@Test
@@ -201,6 +207,10 @@ public void executeTask() throws Exception {
// look up datum stream associated with task
given(datumStreamDao.get(datumStream.getId())).willReturn(datumStream);
+ // resolve datum stream settings
+ given(datumStreamSettingsDao.resolveSettings(TEST_USER_ID, datumStream.getConfigId(),
+ DEFAULT_DATUM_STREAM_SETTINGS)).willReturn(DEFAULT_DATUM_STREAM_SETTINGS);
+
// verify node ownership
final var nodeOwner = new BasicSolarNodeOwnership(datumStream.getObjectId(), TEST_USER_ID, "NZ",
UTC, true, false);
@@ -301,6 +311,10 @@ public void executeTask_notNodeOwner() throws Exception {
// look up datum stream associated with task
given(datumStreamDao.get(datumStream.getId())).willReturn(datumStream);
+ // resolve datum stream settings
+ given(datumStreamSettingsDao.resolveSettings(TEST_USER_ID, datumStream.getConfigId(),
+ DEFAULT_DATUM_STREAM_SETTINGS)).willReturn(DEFAULT_DATUM_STREAM_SETTINGS);
+
// verify node ownership (returning different user, so not owner)
final var nodeOwner = new BasicSolarNodeOwnership(datumStream.getObjectId(), -1L, "NZ", UTC,
true, false);
diff --git a/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/dao/jdbc/test/CinJdbcTestUtils.java b/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/dao/jdbc/test/CinJdbcTestUtils.java
index e7a762a85..b223e9f49 100644
--- a/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/dao/jdbc/test/CinJdbcTestUtils.java
+++ b/solarnet/cloud-integrations/src/test/java/net/solarnetwork/central/c2c/dao/jdbc/test/CinJdbcTestUtils.java
@@ -35,8 +35,10 @@
import net.solarnetwork.central.c2c.domain.CloudDatumStreamMappingConfiguration;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamPollTaskEntity;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamPropertyConfiguration;
+import net.solarnetwork.central.c2c.domain.CloudDatumStreamSettingsEntity;
import net.solarnetwork.central.c2c.domain.CloudDatumStreamValueType;
import net.solarnetwork.central.c2c.domain.CloudIntegrationConfiguration;
+import net.solarnetwork.central.c2c.domain.UserSettingsEntity;
import net.solarnetwork.central.domain.BasicClaimableJobState;
import net.solarnetwork.domain.datum.DatumSamplesType;
import net.solarnetwork.domain.datum.ObjectDatumKind;
@@ -45,7 +47,7 @@
* Helper methods for cloud integrations JDBC tests.
*
* @author matt
- * @version 1.1
+ * @version 1.2
*/
public class CinJdbcTestUtils {
@@ -283,7 +285,7 @@ public static CloudDatumStreamPollTaskEntity newCloudDatumStreamPollTaskEntity(L
}
/**
- * List datum stream configuration rows.
+ * List datum stream poll task rows.
*
* @param jdbcOps
* the JDBC operations
@@ -298,4 +300,80 @@ public static List
*
- *
- * The fact that this implementation extends {@link JdbcNodeServiceAuditor} is a
- * convenience only. All references to "nodes" in this implementation can be
- * thought of as "users".
- *
- *
* @author matt
* @version 1.0
*/
-public class JdbcUserServiceAuditor extends JdbcNodeServiceAuditor implements UserServiceAuditor {
+public class JdbcUserServiceAuditor extends BaseJdbcDatumIdServiceAuditor implements UserServiceAuditor {
/**
* The default value for the {@code nodeServiceIncrementSql} property.
@@ -89,12 +83,25 @@ public JdbcUserServiceAuditor(DataSource dataSource,
ConcurrentMap userServiceCounters, Clock clock,
StatTracker statCounter) {
super(dataSource, userServiceCounters, clock, statCounter);
- setNodeServiceIncrementSql(DEFAULT_USER_SERVICE_INCREMENT_SQL);
+ setServiceIncrementSql(DEFAULT_USER_SERVICE_INCREMENT_SQL);
+ }
+
+ @Override
+ public Clock getAuditClock() {
+ return clock;
}
@Override
public void auditUserService(Long userId, String service, int count) {
- auditNodeService(userId, service, count);
+ if ( count == 0 ) {
+ return;
+ }
+ addServiceCount(DatumId.nodeId(userId, service, clock.instant()), count);
+ }
+
+ @Override
+ public String getPingTestName() {
+ return "JDBC User Service Auditor";
}
}
diff --git a/solarnet/solarjobs/src/main/java/net/solarnetwork/central/jobs/config/HttpClientConfig.java b/solarnet/solarjobs/src/main/java/net/solarnetwork/central/jobs/config/HttpClientConfig.java
index 40dfa4511..4322abe7f 100644
--- a/solarnet/solarjobs/src/main/java/net/solarnetwork/central/jobs/config/HttpClientConfig.java
+++ b/solarnet/solarjobs/src/main/java/net/solarnetwork/central/jobs/config/HttpClientConfig.java
@@ -47,7 +47,7 @@
* HTTP client configuration.
*
* @author matt
- * @version 1.0
+ * @version 1.1
*/
@Configuration(proxyBeanMethods = false)
public class HttpClientConfig {
diff --git a/solarnet/solaruser/src/main/java/net/solarnetwork/central/reg/config/HttpClientConfig.java b/solarnet/solaruser/src/main/java/net/solarnetwork/central/reg/config/HttpClientConfig.java
index 3353c6ae1..04e12dd3e 100644
--- a/solarnet/solaruser/src/main/java/net/solarnetwork/central/reg/config/HttpClientConfig.java
+++ b/solarnet/solaruser/src/main/java/net/solarnetwork/central/reg/config/HttpClientConfig.java
@@ -23,7 +23,8 @@
package net.solarnetwork.central.reg.config;
import java.time.Duration;
-import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@@ -39,13 +40,14 @@
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
+import net.solarnetwork.central.web.support.ContentLengthTrackingClientHttpRequestInterceptor;
import net.solarnetwork.web.jakarta.support.LoggingHttpRequestInterceptor;
/**
* HTTP client configuration.
*
* @author matt
- * @version 1.0
+ * @version 1.1
*/
@Configuration(proxyBeanMethods = false)
public class HttpClientConfig {
@@ -170,7 +172,10 @@ public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory(
@Profile("!http-trace")
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory reqFactory) {
- return new RestTemplate(reqFactory);
+ ThreadLocal tl = ThreadLocal.withInitial(AtomicLong::new);
+ RestTemplate ops = new RestTemplate(reqFactory);
+ ops.setInterceptors(List.of(new ContentLengthTrackingClientHttpRequestInterceptor(tl)));
+ return ops;
}
/**
@@ -183,8 +188,10 @@ public RestTemplate restTemplate(ClientHttpRequestFactory reqFactory) {
@Profile("http-trace")
@Bean
public RestTemplate testingRestTemplate(ClientHttpRequestFactory reqFactory) {
+ ThreadLocal tl = ThreadLocal.withInitial(AtomicLong::new);
RestTemplate debugTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(reqFactory));
- debugTemplate.setInterceptors(Arrays.asList(new LoggingHttpRequestInterceptor()));
+ debugTemplate.setInterceptors(List.of(new ContentLengthTrackingClientHttpRequestInterceptor(tl),
+ new LoggingHttpRequestInterceptor()));
return debugTemplate;
}
From 949823afec91fbcdf6bf147a798d25115e2b263a Mon Sep 17 00:00:00 2001
From: Matt Magoffin
Date: Tue, 29 Oct 2024 12:15:11 +1300
Subject: [PATCH 08/11] Add migration DDL.
---
solarnet-db-setup/postgres/migrations/migrate-20241029.sql | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 solarnet-db-setup/postgres/migrations/migrate-20241029.sql
diff --git a/solarnet-db-setup/postgres/migrations/migrate-20241029.sql b/solarnet-db-setup/postgres/migrations/migrate-20241029.sql
new file mode 100644
index 000000000..09e45b460
--- /dev/null
+++ b/solarnet-db-setup/postgres/migrations/migrate-20241029.sql
@@ -0,0 +1,3 @@
+-- Run this script from the parent directory, e.g. psql -f migrations/migrate-20241029.sql
+
+\i updates/NET-414-cloud-integrations-flux.sql
From df4d3619c655ac04e4bf15c118ef14152e2fda6b Mon Sep 17 00:00:00 2001
From: Matt Magoffin
Date: Tue, 29 Oct 2024 12:27:36 +1300
Subject: [PATCH 09/11] NET-397: add DDL updates.
---
.../postgres/postgres-init-din.sql | 22 ++++++++++---------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/solarnet-db-setup/postgres/postgres-init-din.sql b/solarnet-db-setup/postgres/postgres-init-din.sql
index 5b74ab515..c154c553b 100644
--- a/solarnet-db-setup/postgres/postgres-init-din.sql
+++ b/solarnet-db-setup/postgres/postgres-init-din.sql
@@ -460,7 +460,7 @@ CREATE TABLE solardin.cin_datum_stream_poll_task (
-- index to speed up claim task query
CREATE INDEX cin_datum_stream_poll_task_exec_idx ON solardin.cin_datum_stream_poll_task
- (exec_at) INCLUDE (status);
+ (exec_at DESC) INCLUDE (status);
/**************************************************************************************************
@@ -510,11 +510,12 @@ BEGIN
WHERE status = CASE NEW.enabled WHEN TRUE THEN 'c' ELSE 'q' END
AND user_id = NEW.user_id
AND ds_id IN (
- SELECT id
- FROM solardin.cin_datum_stream
- WHERE user_id = NEW.user_id
- AND int_id = NEW.id
- AND enabled = TRUE
+ SELECT cds.id
+ FROM solardin.cin_datum_stream cds
+ INNER JOIN solardin.cin_datum_stream_map cdsm ON cdsm.id = cds.map_id
+ WHERE cds.user_id = NEW.user_id
+ AND cdsm.int_id = NEW.id
+ AND cds.enabled = TRUE
);
RETURN NEW;
@@ -545,10 +546,11 @@ BEGIN
AND ds_id = NEW.id
AND EXISTS (
SELECT 1
- FROM solardin.cin_integration
- WHERE user_id = NEW.user_id
- AND id = NEW.int_id
- AND enabled = TRUE
+ FROM solardin.cin_datum_stream_map cdsm
+ INNER JOIN solardin.cin_integration ci ON ci.id = cdsm.int_id
+ WHERE ci.user_id = NEW.user_id
+ AND cdsm.id = NEW.map_id
+ AND ci.enabled = TRUE
);
RETURN NEW;
From c5e636810a0c5ebf2da87074c9bd9608348b2b9d Mon Sep 17 00:00:00 2001
From: Matt Magoffin
Date: Tue, 29 Oct 2024 12:28:00 +1300
Subject: [PATCH 10/11] NET-414: incorporate DDL changes.
---
.../postgres/postgres-init-din.sql | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/solarnet-db-setup/postgres/postgres-init-din.sql b/solarnet-db-setup/postgres/postgres-init-din.sql
index c154c553b..36b34d386 100644
--- a/solarnet-db-setup/postgres/postgres-init-din.sql
+++ b/solarnet-db-setup/postgres/postgres-init-din.sql
@@ -304,6 +304,29 @@ CREATE TABLE solardin.inin_endpoint_auth_cred (
Cloud Integrations
============================================ */
+/**
+ * Cloud integration user (account) configuration.
+ *
+ * @column user_id the ID of the account owner
+ * @column id the ID of the configuration
+ * @column created the creation date
+ * @column modified the modification date
+ * @column pub_in a flag to publish datum streams to SolarIn
+ * @column pub_flux a flag to publish datum streams to SolarFlux
+ */
+CREATE TABLE solardin.cin_user_settings (
+ user_id BIGINT NOT NULL,
+ created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ modified TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ pub_in BOOLEAN NOT NULL DEFAULT TRUE,
+ pub_flux BOOLEAN NOT NULL DEFAULT FALSE,
+ CONSTRAINT cin_user_settings_pk PRIMARY KEY (user_id),
+ CONSTRAINT cin_user_settings_user_fk FOREIGN KEY (user_id)
+ REFERENCES solaruser.user_user (id) MATCH SIMPLE
+ ON UPDATE NO ACTION ON DELETE CASCADE
+);
+
+
/**
* Cloud integration configuration.
*
@@ -435,6 +458,30 @@ CREATE TABLE solardin.cin_datum_stream (
);
+/**
+ * Cloud datum stream settings, to override cin_user_settings.
+ *
+ * @column user_id the ID of the account owner
+ * @column ds_id the ID of the datum stream associated with this configuration
+ * @column created the creation date
+ * @column modified the modification date
+ * @column pub_in a flag to publish datum streams to SolarIn
+ * @column pub_flux a flag to publish datum streams to SolarFlux
+ */
+CREATE TABLE solardin.cin_datum_stream_settings (
+ user_id BIGINT NOT NULL,
+ ds_id BIGINT NOT NULL,
+ created TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ modified TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ pub_in BOOLEAN NOT NULL DEFAULT TRUE,
+ pub_flux BOOLEAN NOT NULL DEFAULT TRUE,
+ CONSTRAINT cin_datum_stream_settings_pk PRIMARY KEY (user_id, ds_id),
+ CONSTRAINT cin_datum_stream_settings_ds_fk FOREIGN KEY (user_id, ds_id)
+ REFERENCES solardin.cin_datum_stream (user_id, id) MATCH SIMPLE
+ ON UPDATE NO ACTION ON DELETE CASCADE
+);
+
+
/**
* Cloud datum stream poll task table.
*
From eaecb338eb4dc2ee7f887ea575b2fb76dba2ae6b Mon Sep 17 00:00:00 2001
From: Matt Magoffin
Date: Tue, 29 Oct 2024 12:33:09 +1300
Subject: [PATCH 11/11] Bump versions.
---
solarnet/cloud-integrations/build.gradle | 2 +-
solarnet/common-test/build.gradle | 2 +-
solarnet/common/build.gradle | 2 +-
solarnet/solarjobs/build.gradle | 2 +-
solarnet/solaruser/build.gradle | 2 +-
solarnet/user-cloud-integrations/build.gradle | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/solarnet/cloud-integrations/build.gradle b/solarnet/cloud-integrations/build.gradle
index a54646e8a..72aac88d0 100644
--- a/solarnet/cloud-integrations/build.gradle
+++ b/solarnet/cloud-integrations/build.gradle
@@ -14,7 +14,7 @@ dependencyManagement {
}
description = 'SolarNet: Cloud Integrations'
-version = '1.6.0'
+version = '1.7.0'
base {
archivesName = 'solarnet-cloud-integrations'
diff --git a/solarnet/common-test/build.gradle b/solarnet/common-test/build.gradle
index a4aab5d86..be2c24d07 100644
--- a/solarnet/common-test/build.gradle
+++ b/solarnet/common-test/build.gradle
@@ -14,7 +14,7 @@ dependencyManagement {
}
description = 'SolarNet: Common Test'
-version = '2.5.0'
+version = '2.6.0'
base {
archivesName = 'solarnet-common-test'
diff --git a/solarnet/common/build.gradle b/solarnet/common/build.gradle
index 8b5a39afc..2096ad735 100644
--- a/solarnet/common/build.gradle
+++ b/solarnet/common/build.gradle
@@ -16,7 +16,7 @@ dependencyManagement {
}
description = 'SolarNet: Common'
-version = '2.20.0'
+version = '2.21.0'
base {
archivesName = 'solarnet-common'
diff --git a/solarnet/solarjobs/build.gradle b/solarnet/solarjobs/build.gradle
index 3db78e541..230405cda 100644
--- a/solarnet/solarjobs/build.gradle
+++ b/solarnet/solarjobs/build.gradle
@@ -9,7 +9,7 @@ apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
description = 'SolarJobs'
-version = '2.15.0'
+version = '2.16.0'
base {
archivesName = 'solarjobs'
diff --git a/solarnet/solaruser/build.gradle b/solarnet/solaruser/build.gradle
index 0c7b79514..7045713b8 100644
--- a/solarnet/solaruser/build.gradle
+++ b/solarnet/solaruser/build.gradle
@@ -11,7 +11,7 @@ apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
description = 'SolarUser'
-version = '2.24.0'
+version = '2.25.0'
base {
archivesName = 'solaruser'
diff --git a/solarnet/user-cloud-integrations/build.gradle b/solarnet/user-cloud-integrations/build.gradle
index 0c40fdf58..4cd0d39dd 100644
--- a/solarnet/user-cloud-integrations/build.gradle
+++ b/solarnet/user-cloud-integrations/build.gradle
@@ -14,7 +14,7 @@ dependencyManagement {
}
description = 'SolarNet: User Cloud Integrations'
-version = '1.3.1'
+version = '1.4.0'
base {
archivesName = 'solarnet-user-cloud-integrations'