Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ab2d-6020/Assume Role for Lambda Import/Export #91

Merged
merged 11 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions attribution-data-file-share/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ repositories {

dependencies {
implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.406'
implementation 'org.postgresql:postgresql:42.5.1'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.529'
implementation 'org.postgresql:postgresql:42.7.2'
implementation 'software.amazon.awssdk:s3:2.21.7'
implementation project(path: ':database-management')
implementation 'software.amazon.awssdk:ssm:2.25.7'
implementation 'software.amazon.awssdk:sts:2.25.6'
implementation project(path: ':lambda-lib')
testImplementation 'org.mockito:mockito-core:4.8.0'
testImplementation 'com.mockrunner:mockrunner-jdbc:2.0.7'
Expand Down Expand Up @@ -43,6 +44,12 @@ test {
environment "S3_UPLOAD_PATH", "bfdeft01/test/out"
}

sonarqube {
properties {
property 'sonar.coverage.exclusions', "**/AttributionParameterStore.java"
}
}

task wrapper(type: Wrapper) {
gradleVersion = '7.5'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

public class AttributionDataShareConstants {

private AttributionDataShareConstants() {
}

public static final String ROLE_PARAM = "/ab2d/opt-out/bfd-bucket-role-arn";
public static final String DB_HOST_PARAM = "/ab2d/opt-out/db-host";
public static final String DB_USER_PARAM = "/ab2d/opt-out/db-user";
public static final String DB_PASS_PARAM = "/ab2d/opt-out/db-password";
public static final String ENDPOINT = "https://s3.amazonaws.com";
public static final String TEST_ENDPOINT = "http://127.0.0.1:8001";
public static final Region S3_REGION = Region.US_EAST_1;
public static final String FILE_PATH = "/tmp/";
public static final String REQ_FILE_NAME = "P.AB2D.NGD.REQ.";
public static final String REQ_FILE_NAME_PATTERN = "'D'yyMMdd.'T'hhmmsss";
public static final String REQ_FILE_FORMAT = ".OUT";
public static final String FIRST_LINE = "HDR_BENEDATAREQ_";
public static final String LAST_LINE = "TLR_BENEDATAREQ_";
public static final String SELECT_STATEMENT = "SELECT * FROM public.current_mbi";
Expand All @@ -22,4 +22,7 @@ private AttributionDataShareConstants() {
public static final int EFFECTIVE_DATE_LENGTH = 8;
public static final String BUCKET_NAME_PROP = "S3_UPLOAD_BUCKET";
public static final String UPLOAD_PATH_PROP = "S3_UPLOAD_PATH";

private AttributionDataShareConstants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import gov.cms.ab2d.databasemanagement.DatabaseUtil;
import gov.cms.ab2d.lambdalibs.lib.FileUtil;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider;
import software.amazon.awssdk.services.sts.model.AssumeRoleRequest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;

Expand All @@ -21,34 +25,57 @@
public class AttributionDataShareHandler implements RequestStreamHandler {

// Writes out a file to the FILE_PATH.
// I.E: "P.AB2D.NGD.REQ.D240209.T1122001.OUT"
// I.E: "P.AB2D.NGD.REQ.D240209.T1122001"

public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
LambdaLogger logger = context.getLogger();
logger.log("AttributionDataShare Lambda is started");

String currentDate = new SimpleDateFormat(REQ_FILE_NAME_PATTERN).format(new Date());
String fileName = REQ_FILE_NAME + currentDate + REQ_FILE_FORMAT;
String fileName = REQ_FILE_NAME + currentDate;
String fileFullPath = FILE_PATH + fileName;
var parameterStore = AttributionParameterStore.getParameterStore();
AttributionDataShareHelper helper = helperInit(fileName, fileFullPath, logger);
try {
helper.copyDataToFile(DatabaseUtil.getConnection());
helper.writeFileToFinalDestination(getS3Client(ENDPOINT));
} catch (NullPointerException | URISyntaxException ex) {
try (var dbConnection = DriverManager.getConnection(parameterStore.getDbHost(), parameterStore.getDbUser(), parameterStore.getDbPassword())){

helper.copyDataToFile(dbConnection);
helper.writeFileToFinalDestination(getS3Client(ENDPOINT, parameterStore));

} catch (NullPointerException | URISyntaxException | SQLException ex) {
throwAttributionDataShareException(logger, ex);
} finally {
FileUtil.deleteDirectoryRecursion(Paths.get(fileFullPath));
logger.log("AttributionDataShare Lambda is completed");
}
}

S3Client getS3Client(String endpoint) throws URISyntaxException {
return S3Client.builder()
public S3Client getS3Client(String endpoint, AttributionParameterStore parameterStore) throws URISyntaxException {
var client = S3Client.builder()
.region(S3_REGION)
.endpointOverride(new URI(endpoint))
.build();
}
.endpointOverride(new URI(endpoint));

if (endpoint.equals(ENDPOINT)) {
var stsClient = StsClient
.builder()
.region(S3_REGION)
.build();

var request = AssumeRoleRequest
.builder()
.roleArn(parameterStore.getRole())
.roleSessionName("roleSessionName")
.build();

var credentials = StsAssumeRoleCredentialsProvider
.builder()
.stsClient(stsClient)
.refreshRequest(request)
.build();

client.credentialsProvider(credentials);
}
return client.build();
}
AttributionDataShareHelper helperInit(String fileName, String fileFullPath, LambdaLogger logger) {
return new AttributionDataShareHelper(fileName, fileFullPath, logger);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void copyDataToFile(Connection connection) {
}
}

String getResponseLine(String currentMbi, Timestamp effectiveDate, boolean optOutFlag) {
String getResponseLine(String currentMbi, Timestamp effectiveDate, Boolean optOutFlag) {
var result = new StringBuilder();
result.append(currentMbi);
// Adding spaces to the end of a string to achieve the required position index
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package gov.cms.ab2d.attributionDataShare;

import software.amazon.awssdk.services.ssm.SsmClient;
import software.amazon.awssdk.services.ssm.model.GetParameterRequest;

import static gov.cms.ab2d.attributionDataShare.AttributionDataShareConstants.*;

public class AttributionParameterStore {

private final String role;
private final String dbHost;
private final String dbUser;
private final String dbPassword;

public AttributionParameterStore(String role, String dbHost, String dbUser, String dbPassword) {
this.role = role;
this.dbHost = dbHost;
this.dbUser = dbUser;
this.dbPassword = dbPassword;
}

public static AttributionParameterStore getParameterStore() {
var ssmClient = SsmClient.builder()
.region(S3_REGION)
.build();

var role = getValueFromParameterStore(ROLE_PARAM, ssmClient);
var dbHost = getValueFromParameterStore(DB_HOST_PARAM, ssmClient);
var dbUser = getValueFromParameterStore(DB_USER_PARAM, ssmClient);
var dbPassword = getValueFromParameterStore(DB_PASS_PARAM, ssmClient);

ssmClient.close();
return new AttributionParameterStore(role, dbHost, dbUser, dbPassword);
}

private static String getValueFromParameterStore(String key, SsmClient ssmClient) {
var parameterRequest = GetParameterRequest.builder()
.name(key)
.withDecryption(true)
.build();

var parameterResponse = ssmClient.getParameter(parameterRequest);
return parameterResponse.parameter().value();
}

public String getDbHost() {
return dbHost;
}

public String getDbUser() {
return dbUser;
}

public String getDbPassword() {
return dbPassword;
}

public String getRole() {
return role;
}
}



Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.testcontainers.junit.jupiter.Testcontainers;

import java.net.URISyntaxException;
import java.sql.Connection;
import java.sql.DriverManager;

import static gov.cms.ab2d.attributionDataShare.AttributionDataShareConstants.TEST_ENDPOINT;
import static org.junit.jupiter.api.Assertions.*;
Expand All @@ -21,12 +23,21 @@ class AttributionDataShareHandlerTest {
@Container
private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new AB2DPostgresqlContainer();
LambdaLogger LOGGER = mock(LambdaLogger.class);

AttributionParameterStore parameterStore = new AttributionParameterStore("", "", "", "");
AttributionDataShareHelper helper = mock(AttributionDataShareHelper.class);
AttributionDataShareHandler handler = spy(new AttributionDataShareHandler());

@Test
void attributionDataShareInvoke() {
var mockParameterStore = mockStatic(AttributionParameterStore.class);
mockParameterStore
.when(AttributionParameterStore::getParameterStore)
.thenReturn(parameterStore);

Connection dbConnection = mock(Connection.class);
mockStatic(DriverManager.class)
.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString())).thenReturn(dbConnection);

when(handler.helperInit(anyString(), anyString(), any(LambdaLogger.class))).thenReturn(helper);
assertDoesNotThrow(() -> handler.handleRequest(null, System.out, new TestContext()));
}
Expand All @@ -40,6 +51,6 @@ void attributionDataShareExceptionTest() {

@Test
void getS3ClientTest() throws URISyntaxException {
assertNotNull(handler.getS3Client(TEST_ENDPOINT));
assertNotNull(handler.getS3Client(TEST_ENDPOINT, parameterStore));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
Expand All @@ -17,14 +16,18 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.*;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;

import static gov.cms.ab2d.attributionDataShare.AttributionDataShareConstants.*;
import static gov.cms.ab2d.attributionDataShare.AttributionDataShareHelper.getExecuteQuery;
import static gov.cms.ab2d.attributionDataShare.S3MockAPIExtension.*;
import static gov.cms.ab2d.attributionDataShare.S3MockAPIExtension.getBucketName;
import static gov.cms.ab2d.attributionDataShare.S3MockAPIExtension.getUploadPath;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

Expand All @@ -36,7 +39,7 @@ public class AttributionDataShareTest {
private static final PostgreSQLContainer POSTGRE_SQL_CONTAINER = new AB2DPostgresqlContainer();
LambdaLogger LOGGER = mock(LambdaLogger.class);

String FILE_NAME = REQ_FILE_NAME + new SimpleDateFormat(REQ_FILE_NAME_PATTERN).format(new Date()) + REQ_FILE_FORMAT;
String FILE_NAME = REQ_FILE_NAME + new SimpleDateFormat(REQ_FILE_NAME_PATTERN).format(new Date());
String FILE_FULL_PATH = FILE_PATH + FILE_NAME;

String MBI = "DUMMY000001";
Expand Down Expand Up @@ -65,9 +68,9 @@ void copyDataToFileTest() throws IOException, SQLException {
}

@Test
void getResponseLineTest(){
assertEquals(MBI +" Y", helper.getResponseLine(MBI, null, true));
assertEquals(MBI +"20240226N", helper.getResponseLine(MBI, Timestamp.valueOf("2024-02-26 00:00:00"), false));
void getResponseLineTest() {
assertEquals(MBI + " Y", helper.getResponseLine(MBI, null, true));
assertEquals(MBI + "20240226N", helper.getResponseLine(MBI, Timestamp.valueOf("2024-02-26 00:00:00"), false));
assertEquals("A Y", helper.getResponseLine("A", null, true));
}

Expand All @@ -81,12 +84,12 @@ void writeFileToFinalDestinationTest() throws IOException {
}

@Test
void getBucketNameTest(){
void getBucketNameTest() {
assertEquals(getBucketName(), helper.getBucketName());
}

@Test
void getUploadPathTest(){
void getUploadPathTest() {
assertEquals(getUploadPath(), helper.getUploadPath());
}

Expand Down
18 changes: 12 additions & 6 deletions optout/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ repositories {
dependencies {
implementation 'com.amazonaws:aws-lambda-java-core:1.2.2'
implementation 'com.amazonaws:aws-lambda-java-events:2.2.2'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.406'
implementation 'org.postgresql:postgresql:42.5.1'
implementation 'software.amazon.awssdk:s3:2.21.7'
implementation 'software.amazon.awssdk:secretsmanager:2.23.12'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.529'
implementation 'org.postgresql:postgresql:42.7.2'
implementation 'software.amazon.awssdk:s3:2.25.6'
implementation 'software.amazon.awssdk:ssm:2.25.7'
implementation 'software.amazon.awssdk:sts:2.25.6'
implementation 'com.googlecode.json-simple:json-simple:1.1.1'
implementation project(':database-management')
implementation(project(":lambda-lib"))
testImplementation 'com.amazonaws:aws-java-sdk-sqs:1.11.792'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
testImplementation 'org.mockito:mockito-core:4.8.0'
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'
testImplementation 'org.testcontainers:junit-jupiter:1.19.7'
testImplementation 'org.testcontainers:postgresql:1.19.1'
testImplementation project(':lambda-test-utils')
testImplementation 'org.liquibase:liquibase-core:4.23.0'
Expand All @@ -47,6 +47,12 @@ test {
useJUnitPlatform()
}

sonarqube {
properties {
property 'sonar.coverage.exclusions', "**/OptOutParameterStore.java"
}
}

task wrapper(type: Wrapper) {
gradleVersion = '7.5'
}
Expand Down
7 changes: 5 additions & 2 deletions optout/src/main/java/gov/cms/ab2d/optout/OptOutConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import software.amazon.awssdk.regions.Region;

public class OptOutConstants {
public static final String ROLE_PARAM = "/ab2d/opt-out/bfd-bucket-role-arn";
public static final String DB_HOST_PARAM = "/ab2d/opt-out/db-host";
public static final String DB_USER_PARAM = "/ab2d/opt-out/db-user";
public static final String DB_PASS_PARAM = "/ab2d/opt-out/db-password";
public static final String ENDPOINT = "https://s3.amazonaws.com";
public static final Region S3_REGION = Region.US_EAST_1;
public static final String HEADER_RESP = "HDR_BENEDATARSP";
Expand All @@ -17,10 +21,9 @@ public class OptOutConstants {
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static final String CONF_FILE_NAME = "P.AB2D.NGD.CONF.";
public static final String CONF_FILE_NAME_PATTERN = "'D'yyMMdd.'T'hhmmsss";
public static final String CONF_FILE_FORMAT = ".OUT";
public static final String UPDATE_STATEMENT = "UPDATE public.coverage\n" +
"SET opt_out_flag = ?, effective_date = current_timestamp\n" +
"WHERE current_mbi = ? OR historic_mbis LIKE CONCAT( '%',?,'%')";
"WHERE current_mbi = ? OR historic_mbis iLike CONCAT( '%',?,'%')";


private OptOutConstants() {
Expand Down
4 changes: 0 additions & 4 deletions optout/src/main/java/gov/cms/ab2d/optout/OptOutException.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,4 @@ public class OptOutException extends RuntimeException {
public OptOutException(String errorMessage, Exception exception) {
super(errorMessage, exception);
}

public OptOutException(String errorMessage) {
super(errorMessage);
}
}
Loading
Loading