Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cb11536
Adding functionalilty required to expose the services, unit testing, …
carlospatinos Mar 25, 2018
ca73732
Adding some validations and test for corner cases.
carlospatinos Mar 26, 2018
8d35ebb
Adding a bit of paralellism to the distance calculation. Some perform…
carlospatinos Mar 27, 2018
c4fd625
Adding a bit of paralellism to the distance calculation. Some perform…
carlospatinos Mar 27, 2018
becf9ea
Adding some rational behind the code
carlospatinos Mar 27, 2018
a9ec9b5
Adding some rational behind the code
carlospatinos Mar 27, 2018
3db361d
Adding the right name for the host and in the properties for docker c…
carlospatinos Mar 27, 2018
5d1dbf1
Adding travis for CI
carlospatinos Apr 12, 2018
778330c
Adding the build status tag from travis
carlospatinos Apr 12, 2018
c4e8a9b
Adding test coverage to try it
carlospatinos Apr 13, 2018
f2abcb2
Adding test coverage badge
carlospatinos Apr 14, 2018
2c16f7f
Removing dependency to load mongo embedded
carlospatinos Apr 14, 2018
22564a5
Using covertura instread of jacoco
carlospatinos Apr 14, 2018
9cfd60f
Still cobertura not working, returning to jacoco
carlospatinos Apr 14, 2018
9fc5059
Adding information to the readme about docker-compose up
carlospatinos Apr 15, 2018
e214da4
Adding postman files to improve verification
carlospatinos Apr 15, 2018
f8de697
Adding basic performance testing
carlospatinos Apr 15, 2018
2ba230c
Adding more records for the distance calculation 50 records with 10,0…
carlospatinos Apr 15, 2018
3fb9224
Adding a test when there is no records to generate distance
carlospatinos Apr 15, 2018
350bfe8
Adding some details about building the project.
carlospatinos Apr 15, 2018
c7f5daa
Pulling docker image from docker hub instead of building that locally
carlospatinos Apr 16, 2018
73e77c6
Adding ELK to the project
carlospatinos Apr 18, 2018
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
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TAG=6.2.3
ELASTIC_VERSION=6.2.3
ELASTIC_PASSWORD=changeme
166 changes: 159 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,72 @@
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries

# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml

*.gz

# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
.idea/

# CMake
cmake-build-debug/

# Mongo Explorer plugin:
.idea/**/mongoSettings.xml

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Ruby plugin and RubyMine
/.rakeTasks

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

*.iml
modules.xml
.idea/misc.xml
*.ipr

# Sonarlint plugin
.idea/sonarlint

### Java ###
# Compiled class file
*.class

Expand All @@ -21,15 +90,98 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# IntellijIDEA
.idea/
### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### Gradle ###
.gradle
**/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties



### Eclipse ###

.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/

# Gradle stuff
.gradle/
build/
out/
gradle/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

### Eclipse Patch ###
# Eclipse Core
.project

# JDT-specific (Eclipse Java Development Tools)
.classpath

# Annotation Processing
.apt_generated

11 changes: 11 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
language: java
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/

after_success:
- ./gradlew test jacocoTestReport coveralls
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM openjdk:8-jdk-alpine
EXPOSE 8080
EXPOSE 9010
RUN mkdir -p /app/
ADD build/libs/java-restful-test-0.1.0.jar /app/java-restful-test-0.1.0.jar
#ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
#ENTRYPOINT ["java", "-jar", "/app/java-restful-test-0.1.0.jar"]
ENTRYPOINT ["java", \
"-Dcom.sun.management.jmxremote", \
"-Dcom.sun.management.jmxremote.port=9010", \
"-Dcom.sun.management.jmxremote.local.only=false", \
"-Dcom.sun.management.jmxremote.authenticate=false", \
"-Dcom.sun.management.jmxremote.ssl=false",\
"-Dspring.data.mongodb.uri=mongodb://mongo_db:27017/users",\
"-Djava.security.egd=file:/dev/./urandom",\
"-jar","/app/java-restful-test-0.1.0.jar"]
49 changes: 43 additions & 6 deletions README.MD
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
Welcome to the Java RESTful API test
====================================
[![Build Status](https://travis-ci.org/carlospatinos/Java_RESTful_test.svg?branch=master)](https://travis-ci.org/carlospatinos/Java_RESTful_test)

[![Coverage Status](https://coveralls.io/repos/github/carlospatinos/Java_RESTful_test/badge.svg?branch=master)](https://coveralls.io/github/carlospatinos/Java_RESTful_test?branch=master)

The objetive of this test if to help us evalute your skills with:
**Why?**

* Problem Solving
The objetive of this project is:

* Use some Problem Solving addressing scaling up scenarios
* Web Server API Design
* Request-time data manipulation
* Testing strategies
Expand All @@ -13,16 +17,14 @@ The objetive of this test if to help us evalute your skills with:
**Instructions**

* Fork the repo into a private repo.
* You will need Gradle
* Install Gradle
* You can import the build.gradle file directly on your preferred IDE
* Spring Boot should be used to complete the test, although, if you feel you want to use something different, feel free
* Implement the required API endpoints
* Let us know when you have finished.

**Tasks**

The idea here is for us to see how you design a minimalistic API. This API will be
used to perform CRUD operations on a model called User.
The idea here is for us to see how you design a minimalistic API. This API will be used to perform CRUD operations on a model called User.

You're free to design this model as you want, but, at a minimum should have:

Expand Down Expand Up @@ -127,3 +129,38 @@ Publish your work in a GitHub repository. Feel free to modify this readme to giv
If you need more than 1 day to do this, you might be overthinking, feel free to add improvement notes in your README file, show-off there,
we prefer better quality code if it takes longer, but you must justify this.


**Improvement Notes**
* Currently the application does not have profiles (dev, test, prod) to be able to deploy/test the application accordingly.
With the profile definitions will be good to modify port of the application to use, log level, mongo utl, etc.
* Also some load testing will be good maybe with jmeter to be able to see how much the application can scale or possible buttle necks.
* Profiling the application can be also important now that it is running.
* With the profiles in place, it would be handy to provide the specific profile as a property to the docker file and then to the spring boot application
this will allow the app to behave differently.
* Maybe some code conventions will be useful in big project to standarize things a bit, it brings benefits, obviously define pipelines to make sure code is always running, code reviews, etc.
* Also maybe to have the infrastructure in place for the docker hub to store images and be able to pull them constantly.


**How to run**
To run the code you have to execute.
```sh
docker-compose up
```

This will start mongodb and this service in docker, then you can start sending requests. Then you can use postman or curl to call the end points (url with the format http://host:port). Default port is 8080

You can use postman file provided in the /postman of the project to execute basic calls. Also there is a jemeter project provided which will create 50 users with different coordinates and then call distance 1000 times and then delete everything.

it uses Basic Auth as mechanism for security. User (user) and password (password).

**Answers**
* Answering the questions, I guess we will have a phone call, mut what I did was basically split the functionality in 3 main parts.
1. Create all possible combinations of the users. This allows me to have in memory all different combinations of User -> User to I can calculate the distance
2. Iterate the previous Set of pairs to calculate the distance and store the distance result
3. Based on the distance now I can do all the simple maths for the average, min, max, etc.

This approach allows me for example to use some sort of map reduce mechanism, where I can for instance, generate an independent micro service to calculare the distance (assuming this is the most CPU intensive calculation), then I can use the results from 1 and invoke that service, since that service is independent, I can scale that up as much as I can to parelalize things and split the load among multiple machines if needed, then collect all the results and reduce that to be able to do the average, min, max, etc.


* Normally I dont focus on 100% Test coverage, not really useful to cover setters and getters for instance unless they do something specific.
I rather focus on business logic coverage as high as possible. Saying that, I am totally flexible, if the company requires 100% code coverage I would not have any issue with that.
37 changes: 37 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
import com.bmuschko.gradle.docker.tasks.image.DockerBuildImage

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.0.RELEASE")
classpath('com.bmuschko:gradle-docker-plugin:3.0.8')
}
}

plugins {
id 'jacoco'
id 'com.github.kt3k.coveralls' version '2.6.3'
}

apply plugin: 'jacoco'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.bmuschko.docker-remote-api'

bootJar {
baseName = 'java-restful-test'
version = '0.1.0'
}

jacocoTestReport {
reports {
xml.enabled = true // coveralls plugin depends on xml format report
html.enabled = true
}
}

task createDockerImage(type: DockerBuildImage) {
inputDir = file('.')
tags = ['carlospatinos/java_restful_test']
}

repositories {
mavenCentral()
}
Expand All @@ -26,8 +48,23 @@ sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
compile("org.springframework.boot:spring-boot-devtools")
compile('org.springframework.boot:spring-boot-starter-actuator')
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-data-rest")
compile("org.springframework.boot:spring-boot-starter-data-mongodb")
compile("com.google.guava:guava:24.1-jre")
compile("org.gavaghan:geodesy:1.1.3")
compile("org.apache.commons:commons-lang3:3.7")
compile("org.springframework.boot:spring-boot-starter-security")
//compile("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
//testCompile("cz.jirutka.spring:embedmongo-spring:RELEASE")
testCompile("org.springframework.security:spring-security-test")
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('com.jayway.jsonpath:json-path')
}

test.doLast {
println 'Testing the build from gradle'
createDockerImage
}
3 changes: 3 additions & 0 deletions config/elasticsearch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#network.host: localhost
transport.host: 127.0.0.1
http.host: 0.0.0.0
27 changes: 27 additions & 0 deletions config/httpbeat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
############################## Httpbeat ########################################
httpbeat:
hosts:
# Each - Host endpoints to call. Below are the host endpoint specific configurations
-
schedule: "@every 30s"
url: http://sprinbboot_ws:8080/actuator/health
method: get
headers:
Accept: application/json
output_format: json
json_dot_mode: replace
-
schedule: "@every 30s"
url: http://sprinbboot_ws:8080/actuator/metrics
method: get
headers:
Accept: application/json
output_format: json
json_dot_mode: replace
#================================ General =====================================
fields:
app_id: java-test
#----------------------------- Logstash output --------------------------------
output.elasticsearch:
hosts: ["elasticsearch:9200"]
index: "httpbeat-%{+yyyy.MM.dd}"
2 changes: 2 additions & 0 deletions config/kibana.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
server.host: "0.0.0.0"
elasticsearch.url: "http://elasticsearch:9200"
Loading