All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.dialob.db.sp.DialobDbSpAutoConfiguration Maven / Gradle / Ivy

There is a newer version: 2.2.5
Show newest version
/*
 * Copyright © 2015 - 2021 ReSys ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.dialob.db.sp;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.lang.NonNull;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.dialob.db.assets.AssetFormDatabase;
import io.dialob.db.assets.repository.AssetRepository;
import io.dialob.db.assets.repository.GenericAssetRepository;
import io.dialob.db.assets.serialization.AssetFormDeserializer;
import io.dialob.db.assets.serialization.AssetFormMetadataRowDeserializer;
import io.dialob.db.assets.serialization.AssetFormSerializer;
import io.dialob.db.dialob.api.DialobApiDbSettings;
import io.dialob.db.dialob.api.DialobApiFormDatabase;
import io.dialob.db.dialob.api.DialobApiQuestionnaireDatabase;
import io.dialob.db.dialob.api.DialobApiTemplate;
import io.dialob.db.file.FormFileDatabase;
import io.dialob.db.file.QuestionnaireFileDatabase;
import io.dialob.db.jdbc.DatabaseHelper;
import io.dialob.db.jdbc.JdbcFormDatabase;
import io.dialob.db.jdbc.JdbcQuestionnaireDatabase;
import io.dialob.db.jdbc.JdbcVersionControlledFormDatabase;
import io.dialob.db.jdbc.MySQLDatabaseHelper;
import io.dialob.db.jdbc.PostgreSQLDatabaseHelper;
import io.dialob.db.mongo.MongoQuestionnaireIdObfuscator;
import io.dialob.db.mongo.database.MongoDbFormDatabase;
import io.dialob.db.mongo.database.MongoDbQuestionnaireDatabase;
import io.dialob.db.mongo.repository.FormRepository;
import io.dialob.db.mongo.repository.QuestionnaireRepository;
import io.dialob.db.s3.FormS3Database;
import io.dialob.db.s3.QuestionnaireS3Database;
import io.dialob.db.spi.spring.DatabaseExceptionMapper;
import io.dialob.form.service.api.FormDatabase;
import io.dialob.form.service.api.FormVersionControlDatabase;
import io.dialob.questionnaire.service.api.QuestionnaireDatabase;
import io.dialob.settings.DialobSettings;
import software.amazon.awssdk.services.s3.S3Client;

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(DialobSettings.class)
@Import(DatabaseExceptionMapper.class)
public class DialobDbSpAutoConfiguration {

  @ConditionalOnDatabaseType(DialobSettings.DatabaseType.MONGODB)
  @EnableMongoRepositories
  public static class DialobDbMongoAutoConfiguration {

    @Bean
    @ConditionalOnProperty(prefix = "dialob.formDatabase", name = "database-type", havingValue = "MONGODB", matchIfMissing = true)
    public FormDatabase formDatabase(FormRepository repository) {
      return new MongoDbFormDatabase(repository);
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.questionnaireDatabase", name = "database-type", havingValue = "MONGODB", matchIfMissing = true)
    public QuestionnaireDatabase questionnaireDatabase(QuestionnaireRepository repository, MongoQuestionnaireIdObfuscator mongoQuestionnaireIdObfuscator) {
      return new MongoDbQuestionnaireDatabase(repository, mongoQuestionnaireIdObfuscator);
    }

    @Bean
    @ConditionalOnMissingBean(MongoQuestionnaireIdObfuscator.class)
    public MongoQuestionnaireIdObfuscator mongoQuestionnaireIdObfuscator() {
      return new MongoQuestionnaireIdObfuscator();
    }
  }


  @Configuration(proxyBeanMethods = false)
  @ConditionalOnDatabaseType(DialobSettings.DatabaseType.JDBC)
  public static class DialobDbJdbcAutoConfiguration {

    private final JdbcVersionControlledFormDatabase versionControlledFormDatabase;
    private final JdbcQuestionnaireDatabase jdbcQuestionnaireDatabase;

    private final String schema;

    public DialobDbJdbcAutoConfiguration(TransactionTemplate transactionTemplate,
                                         JdbcTemplate jdbcTemplate,
                                         ObjectMapper objectMapper,
                                         DialobSettings settings) {

      DatabaseHelper databaseHelper = databaseHandler(jdbcTemplate.getDataSource(), settings.getDb().getJdbc().getSchema());

      this.schema = settings.getDb().getJdbc().getSchema();

      Predicate isAnyTenantPredicate = tenantId -> false;
      if (settings.getTenant().getMode() == DialobSettings.TenantSettings.Mode.FIXED) {
        isAnyTenantPredicate = tenantId -> settings.getTenant().getFixedId().equals(tenantId);
      }
      this.versionControlledFormDatabase = new JdbcVersionControlledFormDatabase(jdbcTemplate, this.schema, databaseHelper, transactionTemplate, new JdbcFormDatabase(jdbcTemplate, databaseHelper, transactionTemplate, objectMapper, schema, isAnyTenantPredicate), isAnyTenantPredicate, objectMapper);
      this.jdbcQuestionnaireDatabase = new JdbcQuestionnaireDatabase(jdbcTemplate, databaseHelper, transactionTemplate, objectMapper, this.schema, Optional.of(versionControlledFormDatabase), isAnyTenantPredicate);
    }


    DatabaseHelper databaseHandler(DataSource dataSource, String schema) {
      try (Connection connection = dataSource.getConnection()) {
        final String databaseProductName = connection.getMetaData().getDatabaseProductName();
        switch (databaseProductName) {
          case "PostgreSQL":
            return new PostgreSQLDatabaseHelper(schema);
          case "MySQL":
            return new MySQLDatabaseHelper(schema);
          default:
            throw new IllegalStateException("Unsupported database " + databaseProductName);
        }
      } catch (SQLException e) {
        throw new IllegalStateException(e);
      }
    }

    @Bean
    @ConditionalOnMissingBean(PlatformTransactionManager.class)
    public static PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
      return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    @ConditionalOnMissingBean(TransactionTemplate.class)
    public static TransactionTemplate transactionTemplate(PlatformTransactionManager platformTransactionManager) {
      return new TransactionTemplate(platformTransactionManager);
    }

    // Spring bug here: Spring should not think this as FormDatabase, because return type do not implement type. So, we wrap implementation with proxy
    @Bean
    @ConditionalOnProperty(prefix = "dialob.formDatabase", name = "database-type", havingValue = "JDBC", matchIfMissing = true)
    public FormVersionControlDatabase formVersionControlDatabase() {
      return (FormVersionControlDatabase) Proxy.newProxyInstance(
        this.getClass().getClassLoader(),
        new Class[]{FormVersionControlDatabase.class},
        (o, method, objects) -> {
          try {
            return method.invoke(versionControlledFormDatabase, objects);
          } catch (InvocationTargetException ite) {
            if (ite.getTargetException() instanceof RuntimeException) {
              throw ite.getTargetException();
            }
            throw ite;
          }
        });
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.formDatabase", name = "database-type", havingValue = "JDBC", matchIfMissing = true)
    public FormDatabase formDatabase() {
      return (FormDatabase) Proxy.newProxyInstance(
        this.getClass().getClassLoader(),
        new Class[]{FormDatabase.class},
        (o, method, objects) -> {
          try {
            return method.invoke(versionControlledFormDatabase, objects);
          } catch (InvocationTargetException ite) {
            if (ite.getTargetException() instanceof RuntimeException) {
              throw ite.getTargetException();
            }
            throw ite;
          }
        });
    }

    @Bean
    public QuestionnaireDatabase questionnaireDatabase() {
      return this.jdbcQuestionnaireDatabase;
    }
  }

  @Configuration(proxyBeanMethods = false)
  @ConditionalOnDatabaseType(DialobSettings.DatabaseType.FILEDB)
  public static class DialobDbFileAutoConfiguration {

    private String directory(@NonNull String baseDirectory, @NonNull String type) {
      final File directory = new File(baseDirectory);
      Assert.isTrue(directory.exists(), "File db directory " + baseDirectory + " do not exists");
      Assert.isTrue(directory.isDirectory(), "File db directory " + baseDirectory + " is not directory");
      return directory.toPath().resolve(type).toString();
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.formDatabase", name = "database-type", havingValue = "FILEDB", matchIfMissing = true)
    public FormDatabase formDatabase(ObjectMapper objectMapper, DialobSettings dialobSettings) {
      return new FormFileDatabase(directory(dialobSettings.getDb().getFile().getDirectory(), "forms"), objectMapper);
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.questionnaireDatabase", name = "database-type", havingValue = "FILEDB", matchIfMissing = true)
    public QuestionnaireDatabase questionnaireDatabase(ObjectMapper objectMapper, DialobSettings dialobSettings) {
      return new QuestionnaireFileDatabase(directory(dialobSettings.getDb().getFile().getDirectory(), "questionnaires"), objectMapper);
    }
  }

  @Configuration(proxyBeanMethods = false)
  @ConditionalOnDatabaseType(DialobSettings.DatabaseType.S3)
  public static class DialobDbS3AutoConfiguration {

    @Bean
    @ConditionalOnProperty(prefix = "dialob.formDatabase", name = "database-type", havingValue = "S3", matchIfMissing = true)
    public FormDatabase formDatabase(S3Client s3Client, ObjectMapper objectMapper, DialobSettings settings) {
      return new FormS3Database(s3Client, objectMapper,
        Objects.requireNonNull(settings.getFormDatabase().getS3().getBucket(), "Define S3 bucket for forms"),
        StringUtils.defaultString(settings.getFormDatabase().getS3().getPrefix(), "forms/")
      );
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.questionnaireDatabase", name = "database-type", havingValue = "S3", matchIfMissing = true)
    public QuestionnaireDatabase questionnaireDatabase(S3Client s3Client, ObjectMapper objectMapper, DialobSettings settings) {
      return new QuestionnaireS3Database(s3Client, objectMapper,
        Objects.requireNonNull(settings.getFormDatabase().getS3().getBucket(), "Define S3 bucket for questionnaires"),
        StringUtils.defaultString(settings.getQuestionnaireDatabase().getS3().getPrefix(), "questionnaires/")
      );
    }
  }

  @Configuration(proxyBeanMethods = false)
  @ConditionalOnDatabaseType(DialobSettings.DatabaseType.DIALOBAPIDB)
  @EnableConfigurationProperties(DialobApiDbSettings.class)
  public class DialobDbDialobApiAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(DialobApiTemplate.class)
    public DialobApiTemplate dialobApiTemplate(DialobApiDbSettings settings) {
      RestTemplate restTemplate = new RestTemplate();
      return new DialobApiTemplate(restTemplate, settings);
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.formDatabase", name = "database-type", havingValue = "DIALOBAPIDB", matchIfMissing = true)
    public FormDatabase formDatabase(DialobApiTemplate dialobApiTemplate) {
      return new DialobApiFormDatabase(dialobApiTemplate);
    }

    @Bean
    @ConditionalOnProperty(prefix = "dialob.questionnaireDatabase", name = "database-type", havingValue = "DIALOBAPIDB", matchIfMissing = true)
    public QuestionnaireDatabase questionnaireDatabase(DialobApiTemplate dialobApiTemplate) {
      return new DialobApiQuestionnaireDatabase(dialobApiTemplate);
    }

  }

  @Configuration(proxyBeanMethods = false)
  @ConditionalOnDatabaseType(DialobSettings.DatabaseType.ASSETS)
  public class DialobDbAssetsAutoConfiguration {

    @Bean
    public FormDatabase assetFormDatabase(ObjectMapper objectMapper, DialobSettings settings) {

      RestTemplate restTemplate = new RestTemplate();
      AssetRepository assetRepository = new GenericAssetRepository(restTemplate, settings, objectMapper);

      AssetFormSerializer assetFormSerializer = new AssetFormSerializer(objectMapper);
      AssetFormDeserializer assetFormDeserializer = new AssetFormDeserializer(objectMapper);
      AssetFormMetadataRowDeserializer assetFormMetadataRowDeserializer = new AssetFormMetadataRowDeserializer(objectMapper);

      return new AssetFormDatabase(assetRepository, assetFormSerializer, assetFormDeserializer, assetFormMetadataRowDeserializer);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy