Skip to content

Commit

Permalink
Merge branch 'main' into bug/940-eliminate-warnings-for-missing-fields
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyIR authored Dec 13, 2024
2 parents 0d69e60 + c6a7096 commit e1768bc
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 30 deletions.
3 changes: 2 additions & 1 deletion scripts/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ RS_HOME="/path/to/prime-reportstream"
RS_LCL_API_URL="http://localhost:7071"
RS_STG_API_URL="https://staging.prime.cdc.gov:443"
RS_PRD_API_URL="https://prime.cdc.gov:443"
TI_LCL_API_URL="http://host.docker.internal:8080"
TI_LCL_API_URL="http://localhost:8080"
TI_LCL_API_URL_RS_CONFIG="http://host.docker.internal:8080"
TI_STG_API_URL="https://cdcti-stg-api.azurewebsites.net:443"
TI_PRD_API_URL="https://cdcti-prd-api.azurewebsites.net:443"

Expand Down
34 changes: 30 additions & 4 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
# Scripts
- [Setup](#setup)
- [Available Scripts](#available-scripts)
- [submit.sh](#submitsh)
- [Requirements](#requirements)
- [Usage](#usage)
- [rs.sh](#rssh)
- [Requirements](#requirements-1)
- [Usage](#usage-1)
- [Examples](#examples)
- [ti.sh](#tish)
- [Requirements](#requirements-2)
- [Usage](#usage-2)
- [Examples](#examples-1)
- [ucsd.sh](#ucsdsh)
- [Requirements](#requirements-3)
- [Before running the script](#before-running-the-script)
- [Usage](#usage-3)
- [setup/update-examples-snapshots.sh](#setupupdate-examples-snapshotssh)
- [Requirements](#requirements-4)
- [Usage](#usage-4)
- [setup/setup-reportstream.sh](#setupsetup-reportstreamsh)
- [Requirements](#requirements-5)
- [Usage](#usage-5)
- [lib/common.sh](#libcommonsh)
- [lib/submission-utils.sh](#libsubmission-utilssh)
- [Resources](#resources)

## Setup

Follow the instructions below to load the environments variables required for these scripts
The steps to set up TT and RS locally are in [setup locally section in the main README.md ](../README.md#setup-with-reportstream).
Follow the instructions below to load the environments variables required for these scripts.
ReportStream and Trusted Intermediary must be running locally before running any scripts.

1. Copy `.env.template` to `.env`
```
Expand Down Expand Up @@ -209,7 +235,7 @@ Sends all the HL7 files with `_0_initial_message.hl7` suffix in the `/examples`
### setup/setup-reportstream.sh
Setup script for ReportStream
Setup script for ReportStream.
#### Requirements
Expand Down
15 changes: 13 additions & 2 deletions scripts/setup/setup-reportstream.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ echo "Resetting the database and loading the baseline settings..."
./gradlew reloadTables
./gradlew reloadSettings

# If either TI or RS are not running in docker, then use a gradle hosted URL
local_ti_docker_image_name=$(docker ps --filter "name=trusted-intermediary-router-1" | grep trusted-intermediary-router-1)
local_rs_docker_image_name=$(docker ps --filter "name=prime-router-prime_dev-1" | grep prime-router-prime_dev-1)
if [[ -z $local_ti_docker_image_name || -z $local_rs_docker_image_name ]]; then
ti_api_url=${TI_LCL_API_URL}
echo "No docker instances detected, ReportStream transport will use ${TI_LCL_API_URL}..."
else
ti_api_url=${TI_LCL_API_URL_RS_CONFIG}
echo "Docker instances detected, ReportStream transport will use ${TI_LCL_API_URL_RS_CONFIG}..."
fi

# Need to CD to prime-router to run the prime CLI
cd "prime-router" || exit

Expand All @@ -41,8 +52,8 @@ yq eval '.[0].receivers[] |= (
)' -i "settings/STLTs/Flexion/flexion.yml"

echo "Updating local URL and host in transport settings..."
sed -i '' "s|__TI_API_URL__|${TI_LCL_API_URL}|g" "settings/STLTs/Flexion/flexion.yml"
sed -i '' "s|__TI_API_HOST__|$(extract_host_from_url "${TI_LCL_API_URL}")|g" "settings/STLTs/Flexion/flexion.yml"
sed -i '' "s|__TI_API_URL__|${ti_api_url}|g" "settings/STLTs/Flexion/flexion.yml"
sed -i '' "s|__TI_API_HOST__|$(extract_host_from_url "${ti_api_url}")|g" "settings/STLTs/Flexion/flexion.yml"

echo "Updating transport settings in partner org files..."
for file in "settings/STLTs/CA/ucsd.yml" "settings/STLTs/LA/la-ochsner.yml" "settings/STLTs/LA/la-phl.yml"; do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class ApplicationContext {
protected static final Map<String, String> TEST_ENV_VARS = new ConcurrentHashMap<>();
protected static final Set<Object> IMPLEMENTATIONS = new HashSet<>();

protected static boolean skipMissingImplementations = false;

protected ApplicationContext() {}

public static void register(Class<?> clazz, Object implementation) {
Expand All @@ -53,17 +55,39 @@ public static <T> Set<Class<? extends T>> getImplementors(Class<T> interfaze) {
}

public static void injectRegisteredImplementations() {
injectRegisteredImplementations(false);
doInjectRegisteredImplementations();
}

protected static void injectRegisteredImplementations(boolean skipMissingImplementations) {
protected static void doInjectRegisteredImplementations() {
var fields = Reflection.getFieldsAnnotatedWith(Inject.class);

fields.forEach(field -> injectIntoField(field, skipMissingImplementations));
fields.forEach(ApplicationContext::injectIntoField);
}

public static void injectIntoNonSingleton(Object instance) {
var fields = Reflection.getFieldsAnnotatedWithInstance(instance.getClass(), Inject.class);

fields.forEach(field -> injectIntoField(field, instance));
}

private static void injectIntoField(Field field, boolean skipMissingImplementations) {
private static void injectIntoField(Field field, Object instance) {
var fieldType = field.getType();

Object fieldImplementation = getFieldImplementation(fieldType);
if (fieldImplementation == null) {
return;
}

field.trySetAccessible();
try {
field.set(instance, fieldImplementation);
} catch (IllegalAccessException | IllegalArgumentException exception) {
throw new IllegalArgumentException(
"unable to inject " + fieldType + " into " + instance.getClass(), exception);
}
}

private static void injectIntoField(Field field) {
var declaringClass = field.getDeclaringClass();

if (!IMPLEMENTATIONS.contains(declaringClass)) {
Expand All @@ -76,29 +100,16 @@ private static void injectIntoField(Field field, boolean skipMissingImplementati
declaringClassesToTry.add(declaringClass);
declaringClassesToTry.addAll(Arrays.asList(declaringClass.getInterfaces()));

Object fieldImplementation = getFieldImplementation(fieldType, skipMissingImplementations);
if (fieldImplementation == null) {
return;
}

Object declaringClassImplementation =
getDeclaringClassImplementation(declaringClassesToTry, skipMissingImplementations);
getDeclaringClassImplementation(declaringClassesToTry);
if (declaringClassImplementation == null) {
return;
}

field.trySetAccessible();

try {
field.set(declaringClassImplementation, fieldImplementation);
} catch (IllegalAccessException | IllegalArgumentException exception) {
throw new IllegalArgumentException(
"Unable to inject " + fieldType + " into " + declaringClass, exception);
}
injectIntoField(field, declaringClassImplementation);
}

private static Object getFieldImplementation(
Class<?> fieldType, boolean skipMissingImplementations) {
private static Object getFieldImplementation(Class<?> fieldType) {
Object fieldImplementation;

try {
Expand All @@ -116,8 +127,7 @@ private static Object getFieldImplementation(
return fieldImplementation;
}

private static Object getDeclaringClassImplementation(
List<Class<?>> declaringClassesToTry, boolean skipMissingImplementations) {
private static Object getDeclaringClassImplementation(List<Class<?>> declaringClassesToTry) {
Object declaringClassImplementation =
declaringClassesToTry.stream()
.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import static org.reflections.scanners.Scanners.FieldsAnnotated;
import static org.reflections.scanners.Scanners.SubTypes;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import org.reflections.Reflections;

/**
Expand All @@ -27,4 +30,10 @@ public static <T> Set<Class<? extends T>> getImplementors(Class<T> interfaze) {
public static Set<Field> getFieldsAnnotatedWith(Class<?> annotation) {
return REFLECTIONS.get(FieldsAnnotated.with(annotation).as(Field.class));
}

public static Set<Field> getFieldsAnnotatedWithInstance(Class<?> clazz, Class<?> annotation) {
return Arrays.stream(clazz.getDeclaredFields())
.filter(field -> field.isAnnotationPresent(annotation.asSubclass(Annotation.class)))
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gov.hhs.cdc.trustedintermediary.context

import gov.hhs.cdc.trustedintermediary.wrappers.Logger
import spock.lang.Specification

import javax.inject.Inject
Expand All @@ -8,6 +9,34 @@ import java.nio.file.Paths

class ApplicationContextTest extends Specification {

interface TestingInterface {
void test()
}

class NonSingletonClazz {
@Inject
Logger logger
void test() {}
}

static class DogCow implements TestingInterface {

@Override
void test() {
print("test()")
}
}

static class DogCowTwo implements TestingInterface {

@Override
void test() {
print("testTwo()")
}
}
def DOGCOW = new DogCow()
def DOGCOWTWO = new DogCowTwo()

def setup() {
TestApplicationContext.reset()
}
Expand All @@ -21,6 +50,48 @@ class ApplicationContextTest extends Specification {
result == ApplicationContext.getImplementation(String.class)
}

def "implementors retrieval test"() {
setup:
def dogCow = DOGCOW
def dogCowTwo = DOGCOWTWO
def implementors = new HashSet()
implementors.add(DogCow)
implementors.add(DogCowTwo)

expect:
implementors == ApplicationContext.getImplementors(TestingInterface)
}

def "injectIntoNonSingleton unhappy path"() {
given:
def nonSingletonClass = new NonSingletonClazz()
def object = new Object()
ApplicationContext.register(Logger, object)
when:
ApplicationContext.injectIntoNonSingleton(nonSingletonClass)
then:
thrown(IllegalArgumentException)
}

def "injectIntoNonSingleton unhappy path when fieldImplementation runs into an error"() {
given:
def nonSingletonClass = new NonSingletonClazz()
when:
ApplicationContext.injectIntoNonSingleton(nonSingletonClass)
then:
thrown(IllegalArgumentException)
}

def "injectIntoNonSingleton unhappy path when fieldImplementation is null"() {
given:
def nonSingletonClass = new NonSingletonClazz()
when:
ApplicationContext.skipMissingImplementations = true
ApplicationContext.injectIntoNonSingleton(nonSingletonClass)
then:
noExceptionThrown()
}

def "implementation injection test"() {
given:
def injectedValue = "DogCow"
Expand Down Expand Up @@ -133,6 +204,25 @@ class ApplicationContextTest extends Specification {
Files.deleteIfExists(directoryPath)
}

def "registering an unsupported injection class"() {
given:
def injectedValue = "DogCow"
def injectionInstantiation = new InjectionDeclaringClass()

TestApplicationContext.register(List.class, injectionInstantiation)
// notice above that I'm registering the injectionInstantiation object as a List class.
// injectionInstantiation is of class InjectionDeclaringClass,
// and InjectionDeclaringClass doesn't implement List (it only implements AFieldInterface).
TestApplicationContext.register(String.class, injectedValue)

when:
TestApplicationContext.injectRegisteredImplementations()
injectionInstantiation.getAField()

then:
noExceptionThrown()
}

class InjectionDeclaringClass {
@Inject
private String aField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class TestApplicationContext extends ApplicationContext {
}

def static injectRegisteredImplementations() {
injectRegisteredImplementations(true)
skipMissingImplementations = true
ApplicationContext.injectRegisteredImplementations()
}

def static addEnvironmentVariable(String key, String value) {
Expand Down

0 comments on commit e1768bc

Please sign in to comment.