From 5d489cc49396cfc85bafcb2660b5724e7ddd32b9 Mon Sep 17 00:00:00 2001 From: Marc Nuri Date: Mon, 29 Jul 2019 07:11:37 +0200 Subject: [PATCH] #317: Added Google Analytics Tracking Id to server configuration --- server/README.md | 17 +++++ .../api/application/ApplicationResource.java | 1 + .../api/application/ConfigurationDto.java | 27 +++++++ .../IsotopeApiConfiguration.java | 6 ++ .../isotope/api/imap/ImapService.java | 2 +- .../application/ApplicationResourceTest.java | 15 ++++ .../api/application/ConfigurationDtoTest.java | 74 +++++++++++++++++++ .../IsotopeApiConfigurationTest.java | 69 +++++++++++++++++ 8 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 server/README.md create mode 100644 server/src/test/java/com/marcnuri/isotope/api/application/ConfigurationDtoTest.java create mode 100644 server/src/test/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfigurationTest.java diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..a6738a9 --- /dev/null +++ b/server/README.md @@ -0,0 +1,17 @@ +Isotope Mail Client (Server) +============================ + +HTTP REST API for IMAP/SMTP communications. + +Java Back-end application designed as a "microservice", future developments +will break down this application into separate microservices for communications with +different protocols (IMAP, SMTP, etc.) and data domains. + +## Configuration variables + +Variable | Description +-------- | ----------- +ENCRYPTION_PASSWORD | Password used for encryption and decryption (symmetric) of credentials (Password must be the same for all the instances of the microservice in a single deployment) +TRUSTED_HOSTS | Comma separated list of hosts allowed as IMAP/SMTP server parameters, if empty or not provided, all hosts will be allowed. +EMBEDDED_IMAGE_SIZE_THRESHOLD | Size in bytes defining the threshold value used to decide if images will be downloaded separately or embedded within messages when retrieved from the client +GOOGLE_ANALYTICS_TRACKING_ID | \[Optional\] Google Analytics tracking id to enable google analytics in client application diff --git a/server/src/main/java/com/marcnuri/isotope/api/application/ApplicationResource.java b/server/src/main/java/com/marcnuri/isotope/api/application/ApplicationResource.java index f941998..14bbfa2 100644 --- a/server/src/main/java/com/marcnuri/isotope/api/application/ApplicationResource.java +++ b/server/src/main/java/com/marcnuri/isotope/api/application/ApplicationResource.java @@ -99,6 +99,7 @@ public ResponseEntity login( @SuppressWarnings("ConstantConditions") private ConfigurationDto toDto(IsotopeApiConfiguration configuration) { final ConfigurationDto ret = new ConfigurationDto(); + ret.setGoogleAnalyticsTrackingId(configuration.getGoogleAnalyticsTrackingId()); ret.add(linkTo(methodOn(ApplicationResource.class) .login(null)) .withRel(REL_APPLICATION_LOGIN)); diff --git a/server/src/main/java/com/marcnuri/isotope/api/application/ConfigurationDto.java b/server/src/main/java/com/marcnuri/isotope/api/application/ConfigurationDto.java index cab977c..70c26ef 100644 --- a/server/src/main/java/com/marcnuri/isotope/api/application/ConfigurationDto.java +++ b/server/src/main/java/com/marcnuri/isotope/api/application/ConfigurationDto.java @@ -20,14 +20,41 @@ */ package com.marcnuri.isotope.api.application; +import com.fasterxml.jackson.annotation.JsonInclude; import com.marcnuri.isotope.api.resource.IsotopeResource; import java.io.Serializable; +import java.util.Objects; /** * Created by Marc Nuri on 2019-04-06. */ +@JsonInclude(JsonInclude.Include.NON_NULL) class ConfigurationDto extends IsotopeResource implements Serializable { private static final long serialVersionUID = -1279556906780840837L; + + private String googleAnalyticsTrackingId; + + public String getGoogleAnalyticsTrackingId() { + return googleAnalyticsTrackingId; + } + + public void setGoogleAnalyticsTrackingId(String googleAnalyticsTrackingId) { + this.googleAnalyticsTrackingId = googleAnalyticsTrackingId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + ConfigurationDto that = (ConfigurationDto) o; + return Objects.equals(googleAnalyticsTrackingId, that.googleAnalyticsTrackingId); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), googleAnalyticsTrackingId); + } } diff --git a/server/src/main/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfiguration.java b/server/src/main/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfiguration.java index 648e5e6..01ff302 100644 --- a/server/src/main/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfiguration.java +++ b/server/src/main/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfiguration.java @@ -56,6 +56,8 @@ public class IsotopeApiConfiguration { private static final String EMBEDDED_IMAGE_SIZE_THRESHOLD = "EMBEDDED_IMAGE_SIZE_THRESHOLD"; private static final long EMBEDDED_IMAGE_SIZE_THRESHOLD_DEFAULT_50KB = 51200L; + private static final String GOOGLE_ANALYTICS_TRACKING_ID = "GOOGLE_ANALYTICS_TRACKING_ID"; + private static final int CREDENTIALS_DURATION_MINUTES = 15; private static final int CREDENTIALS_REFRESH_BEFORE_DURATION_MINUTES = 10; @@ -87,6 +89,10 @@ public long getEmbeddedImageSizeThreshold() { return environment.getProperty(EMBEDDED_IMAGE_SIZE_THRESHOLD, Long.class, EMBEDDED_IMAGE_SIZE_THRESHOLD_DEFAULT_50KB); } + public String getGoogleAnalyticsTrackingId() { + return environment.getProperty(GOOGLE_ANALYTICS_TRACKING_ID); + } + public TemporalAmount getCredentialsDuration() { return Duration.ofMinutes(CREDENTIALS_DURATION_MINUTES); } diff --git a/server/src/main/java/com/marcnuri/isotope/api/imap/ImapService.java b/server/src/main/java/com/marcnuri/isotope/api/imap/ImapService.java index f598f93..df7642c 100644 --- a/server/src/main/java/com/marcnuri/isotope/api/imap/ImapService.java +++ b/server/src/main/java/com/marcnuri/isotope/api/imap/ImapService.java @@ -563,7 +563,7 @@ private void setMessagesFlag(URLName folderId, Flags.Flag flag, boolean flagValu } /** - * Returns a list of {@link Attachment}s and replaces embedded images in {@link Message#content} if they are + * Returns a list of {@link Attachment}s and replaces embedded images in {@link Message#getContent()} if they are * small in order to avoid future calls to the API which may result more expensive. * * @param finalMessage diff --git a/server/src/test/java/com/marcnuri/isotope/api/application/ApplicationResourceTest.java b/server/src/test/java/com/marcnuri/isotope/api/application/ApplicationResourceTest.java index 9321bc7..6fbc147 100644 --- a/server/src/test/java/com/marcnuri/isotope/api/application/ApplicationResourceTest.java +++ b/server/src/test/java/com/marcnuri/isotope/api/application/ApplicationResourceTest.java @@ -44,6 +44,7 @@ import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; @@ -86,6 +87,7 @@ public void getConfiguration_na_shouldReturnOk() throws Exception { .accept(MediaTypes.HAL_JSON_VALUE)); // Then result.andExpect(status().isOk()); + result.andExpect(jsonPath("$.googleAnalyticsTrackingId").doesNotExist()); result.andExpect(jsonPath("$._links").exists()); result.andExpect(jsonPath("$._links", aMapWithSize(14))); result.andExpect(jsonPath("$._links['application.login'].href", endsWith("/v1/application/login"))); @@ -109,6 +111,19 @@ public void getConfiguration_na_shouldReturnOk() throws Exception { result.andExpect(jsonPath("$._links.smtp.href", endsWith("/v1/smtp"))); } + @Test + public void getConfiguration_optionalEnvVariablesSet_shouldReturnOk() throws Exception { + // Given + doReturn("UA-1337-33").when(isotopeApiConfiguration).getGoogleAnalyticsTrackingId(); + // When + final ResultActions result = mockMvc.perform(get("/v1/application/configuration") + .accept(MediaTypes.HAL_JSON_VALUE)); + // Then + result.andExpect(status().isOk()); + result.andExpect(jsonPath("$.googleAnalyticsTrackingId", is("UA-1337-33"))); + result.andExpect(jsonPath("$._links", aMapWithSize(14))); + } + @Test public void login_validCredentials_shouldReturnOk() throws Exception { // Given diff --git a/server/src/test/java/com/marcnuri/isotope/api/application/ConfigurationDtoTest.java b/server/src/test/java/com/marcnuri/isotope/api/application/ConfigurationDtoTest.java new file mode 100644 index 0000000..f883b70 --- /dev/null +++ b/server/src/test/java/com/marcnuri/isotope/api/application/ConfigurationDtoTest.java @@ -0,0 +1,74 @@ +/* + * ConfigurationDtoTest.java + * + * Created on 2019-07-29, 6:58 + * + * Copyright 2019 Marc Nuri + * + * 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 com.marcnuri.isotope.api.application; + +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +/** + * Created by Marc Nuri on 2019-07-29. + */ +public class ConfigurationDtoTest { + + @Test + public void hashCode_sameProperties_shouldBeTrue() { + // Given + final ConfigurationDto one = new ConfigurationDto(); + final ConfigurationDto other = new ConfigurationDto(); + for (ConfigurationDto instance : new ConfigurationDto[]{one, other}) { + instance.setGoogleAnalyticsTrackingId("UA-1337-33"); + } + // When + final int hashCodeOne = one.hashCode(); + final int hashCodeOther = other.hashCode(); + // Then + assertThat(hashCodeOne, is(hashCodeOther)); + } + + @Test + public void equals_sameProperties_shouldBeTrue() { + // Given + final ConfigurationDto one = new ConfigurationDto(); + final ConfigurationDto other = new ConfigurationDto(); + for (ConfigurationDto instance : new ConfigurationDto[]{one, other}) { + instance.setGoogleAnalyticsTrackingId("UA-1337-33"); + } + // When + final boolean result = one.equals(other); + // Then + assertThat(result, is(true)); + } + + @Test + public void equals_differentProperties_shouldBeFalse() { + // Given + final ConfigurationDto one = new ConfigurationDto(); + one.setGoogleAnalyticsTrackingId("UA-1337-33"); + final ConfigurationDto other = new ConfigurationDto(); + // When + final boolean result = one.equals(other); + // Then + assertThat(result, is(false)); + } + +} diff --git a/server/src/test/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfigurationTest.java b/server/src/test/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfigurationTest.java new file mode 100644 index 0000000..ece8f0d --- /dev/null +++ b/server/src/test/java/com/marcnuri/isotope/api/configuration/IsotopeApiConfigurationTest.java @@ -0,0 +1,69 @@ +/* + * IsotopeApiConfigurationTest.java + * + * Created on 2019-07-28, 20:34 + * + * Copyright 2019 Marc Nuri + * + * 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 com.marcnuri.isotope.api.configuration; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; + +/** + * Created by Marc Nuri on 2019-07-28. + */ +public class IsotopeApiConfigurationTest { + + private Environment environment; + private IsotopeApiConfiguration istotopeApiConfiguration; + + @Before + public void setUp() { + environment = Mockito.mock(Environment.class); + istotopeApiConfiguration = new IsotopeApiConfiguration(environment); + } + + @Test + public void getEncryptionPassword_envVariableSet_shouldReturnEnvValue() { + // Given + doReturn("1234").when(environment) + .getProperty(eq("ENCRYPTION_PASSWORD"), anyString()); + // When + final String result = istotopeApiConfiguration.getEncryptionPassword(); + // Then + assertThat(result, is("1234")); + } + + @Test + public void getGoogleAnalyticsTrackingId_envVariableSet_shouldReturnEnvValue() { + // Given + doReturn("UA-1337-33").when(environment).getProperty(eq("GOOGLE_ANALYTICS_TRACKING_ID")); + // When + final String result = istotopeApiConfiguration.getGoogleAnalyticsTrackingId(); + // Then + assertThat(result, is("UA-1337-33")); + } + +}