Skip to content
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
50 changes: 42 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ This repository contains the back end for the Zerobase smart tracing platform. R
Any commands shown in this read-me are written in bash. If you are on Windows or use an alternate shell, like fish, please adjust the commands
accordingly.

## Set up for the project
There are other README files throughout the project. They are located in the source folders that most closely align with the
readme contents.

## Development Setup

### Kotlin
The backend is written in Kotlin. While you can work on it in any editor, such as vim or VS Code, it is significantly easier to use an IDE. We recommend [IntelliJ](https://www.jetbrains.com/idea/download/index.html).
Expand All @@ -26,11 +29,23 @@ managers, such as `brew` on macOS, for easy updating (recommended way) but can a
* Follow these installation instructions [here](https://maven.apache.org/install.html)
* Add the full path to your ~/.bash_profile, ~/.zshrc, or similar.


### Docker
Docker is used to run a graph database while running the project locally.
Docker is used to run external services locally for front-end development or tests instead of requiring access to a cloud environment.
Some of the resources can be run without docker, but some don't really have local install options. Install docker manually by following
this [guide](https://www.docker.com/get-started) or via a package manager.

## Running Locally for Front-end Development or Testing

* Install docker manually by following this [guide](https://www.docker.com/get-started) or via a package manager.
### Docker Compose
We have a docker compose file that will spin up all the pieces necessary and expose the API on your local machine. There are several
environment variables that can be used to configure it:

* `DB_PORT`: Used if you want to connect to the gremlin server manually and play with the graph.
* `APP_VERSION`: By default, it will use latest. If you need to run a specific version, or one that isn't in DockerHub yet, set this to the version tag you want.
* `APP_PORT`: By default, it will be 9000.

### Manually with Docker
#### Database - Gremlin
* Pull the Gremlin server image
```sh
$ docker pull tinkerpop/gremlin-server
Expand All @@ -46,18 +61,35 @@ Docker is used to run a graph database while running the project locally.
```
By default, there are no credentials for the local install

#### Localstack - AWS Fakes
Follow their startup documentation: https://github.com/localstack/localstack.

#### App
```sh
$ docker run -d --name=zerobase-api \
-e WRITE_ENDPOINT=<gremlin-api host> \
-e DB_PORT=<gremlin mapped port> \
-e AWS_SES_ENDPOINT=<localstack ses http url (including port)>
zerobaseio/smart-tracing-api:<version>
```

### Manually without Docker
#### Run Gremlin without Docker
If you're unable to use Docker to run Gremlin, there is an alternative available. The desktop version can be downloaded
from [here](https://www.apache.org/dyn/closer.lua/tinkerpop/3.4.6/apache-tinkerpop-gremlin-server-3.4.6-bin.zip). The configuration
instructions are found [here](http://tinkerpop.apache.org/docs/3.4.6/reference/#gremlin-server). The default configuration should
suffice.

## Project
#### AWS SES
Follow the documentation for a non-Docker SES fake. Here's one: https://github.com/csi-lk/aws-ses-local

#### Project
After cloning the project there are two ways to deploy it locally: using an IDE or via the command line. By default, the app listens on
port 9000. You can override that with an environment variable of `PORT` if you need to. The `local-config.yml` defaults to `localhost`
and `8182` for the database. Both can be overriden with environment variables, using `DB_HOST` and `DB_PORT` respectively.
and `8182` for the database. Both can be overriden with environment variables, using `WRITE_ENDPOINT` and `DB_PORT` respectively.

**Running in an IDE**

### Running in an IDE
The following directions use Intellij as the IDE, but the steps should be similar if you are using a different IDE.

* If necessary, navigate to File/Project Structure. Update the SDK for the project to JDK11.
Expand All @@ -78,7 +110,7 @@ set an environment variable of `DB_PORT` to the port your gremlin server is runn

* Click `OK` and run the configuration you just made.

### Running from the command line.
**Running from the command line.**
* From the project's root directory, build the project.
```sh
$ mvn clean install
Expand All @@ -93,8 +125,10 @@ set an environment variable of `DB_PORT` to the port your gremlin server is runn
```sh
$ PORT=8888 java -jar target/smart-tracing-api.jar server target/classes/local-config.yml
```

### Debugging / Calling end points
* Once you're running the project, you can double check if all is well by visiting `http://localhost:8081` in your browser. You
shoudl see a simple page that exposes metrics and healthcheck results. You can also curl `http://localhost:8081/healthchecks` if
you prefer the command line.
* App endpoints are available at `http://localhost:9000`

30 changes: 30 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
version: "3.7"

services:

database:
image: tinkerpop/gremlin-server:latest
ports:
- ${DB_PORT:-8182}

aws:
image: localstack/localstack:latest
environment:
- SERVICES=ses
- DEBUG=${AWS_DEBUG:-false}

app:
image: zerobaseio/smart-tracing-api:${APP_VERSION:-latest}
ports:
- ${APP_PORT:-9000}
depends_on:
- database
- aws
environment:
- WRITE_ENDPOINT=database
- READ_ENDPOINT=database
- DB_PORT=8182
- AWS_SES_ENDPOINT=http://aws:4579
- PORT=${APP_PORT:-9000}


Binary file added examples/example-zerobase-qr.pdf
Binary file not shown.
Binary file added images/qr_logo_overlay.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/zerobase_screenshot_pdf.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pdfs/zerobase-qr.pdf
Binary file not shown.
47 changes: 39 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
<properties>
<kotlin.version>1.3.70</kotlin.version>
<basepom.shaded.main-class>io.zerobase.smarttracing.MainKt</basepom.shaded.main-class>
<dropwizard.version>2.0.2</dropwizard.version>
<dropwizard.version>2.0.5</dropwizard.version>
<gremlin.version>[3.4.6,4)</gremlin.version>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<kotlin.compiler.jvmTarget>11</kotlin.compiler.jvmTarget>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -220,7 +220,6 @@
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand All @@ -244,10 +243,6 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
Expand All @@ -264,9 +259,45 @@
<artifactId>jakarta.activation</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>net.sf.jtidy</groupId>
<artifactId>jtidy</artifactId>
<version>r938</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf-openpdf</artifactId>
<version>9.1.20</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.0</version>
</dependency>

<!-- Test Dependencies -->

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/io/zerobase/smarttracing/GraphDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import io.zerobase.smarttracing.utils.LoggerDelegate
import org.apache.tinkerpop.gremlin.process.traversal.Traversal
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.apache.tinkerpop.gremlin.structure.T
import org.apache.tinkerpop.gremlin.structure.VertexProperty
import java.util.*

private fun <S, T> Traversal<S, T>.getIfPresent(): T? {
Expand Down
41 changes: 33 additions & 8 deletions src/main/kotlin/io/zerobase/smarttracing/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import io.dropwizard.configuration.SubstitutingSourceProvider
import io.dropwizard.setup.Bootstrap
import io.dropwizard.setup.Environment
import io.zerobase.smarttracing.config.GraphDatabaseFactory
import io.zerobase.smarttracing.notifications.AmazonEmailSender
import io.zerobase.smarttracing.notifications.NotificationFactory
import io.zerobase.smarttracing.notifications.NotificationManager
import io.zerobase.smarttracing.pdf.DocumentFactory
import io.zerobase.smarttracing.resources.*
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.eclipse.jetty.servlets.CrossOriginFilter
import java.util.*
import javax.servlet.DispatcherType
import javax.servlet.FilterRegistration
import org.thymeleaf.TemplateEngine
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver
import org.w3c.tidy.Tidy
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.ses.SesClient
import com.github.mustachejava.DefaultMustacheFactory
import io.zerobase.smarttracing.notifications.AmazonEmailSender
import io.zerobase.smarttracing.notifications.NotificationFactory
import io.zerobase.smarttracing.notifications.NotificationManager
import java.net.URI
import java.nio.charset.StandardCharsets
import java.util.*
import javax.mail.Session
import javax.servlet.DispatcherType
import javax.servlet.FilterRegistration

typealias MultiMap<K,V> = Map<K, List<V>>

Expand Down Expand Up @@ -66,7 +70,28 @@ class Main: Application<Config>() {
config.aws.ses.endpoint?.let(sesClientBuilder::endpointOverride)
val emailSender = AmazonEmailSender(sesClientBuilder.build(), session, config.notifications.email.fromAddress)
val notificationManager = NotificationManager(emailSender)
val notificationFactory = NotificationFactory(DefaultMustacheFactory(config.notifications.templateLocation))
val notificationFactory = NotificationFactory(TemplateEngine().apply {
templateResolvers = setOf(ClassLoaderTemplateResolver().apply {
prefix = "/notifications"
suffix = ".html"
characterEncoding = StandardCharsets.UTF_8.displayName()
})
})

val resolver = ClassLoaderTemplateResolver().apply {
prefix = "/pdfs"
suffix = ".html"
characterEncoding = StandardCharsets.UTF_8.displayName()
}
val templateEngine = TemplateEngine().apply {
templateResolvers = setOf(resolver)
}

val documentFactory = DocumentFactory(templateEngine, Tidy().apply {
inputEncoding = StandardCharsets.UTF_8.displayName()
outputEncoding = StandardCharsets.UTF_8.displayName()
xhtml = true
})

val dao = GraphDao(graph, phoneUtil)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package io.zerobase.smarttracing.notifications

import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.ses.SesClient
import software.amazon.awssdk.services.ses.model.RawMessage
import software.amazon.awssdk.services.ses.model.SendRawEmailRequest
import java.io.ByteArrayOutputStream
import java.nio.charset.StandardCharsets
import javax.activation.DataHandler
import javax.mail.Message
import javax.mail.Session
import javax.mail.internet.InternetAddress
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMessage
import javax.mail.internet.MimeMultipart
import javax.mail.internet.MimeBodyPart
import javax.mail.util.ByteArrayDataSource
import java.io.ByteArrayOutputStream
import javax.activation.DataHandler

import software.amazon.awssdk.services.ses.SesClient
import software.amazon.awssdk.core.SdkBytes
import software.amazon.awssdk.services.ses.model.*
import java.nio.charset.StandardCharsets

class AmazonEmailSender(
private val client: SesClient,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.zerobase.smarttracing.notifications

import com.github.mustachejava.MustacheFactory
import com.google.common.net.MediaType
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings
import org.thymeleaf.TemplateEngine

enum class NotificationMedium {
EMAIL
}

class NotificationFactory(private val templateFactory: MustacheFactory) {
class NotificationFactory(private val templateEngine: TemplateEngine) {

}

Expand Down
26 changes: 26 additions & 0 deletions src/main/kotlin/io/zerobase/smarttracing/notifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Notifications
We are (or will be) sending a variety of notifications through (eventually) a variety of mediums. It's just email for now,
but SMS is on the road map, and we may have other options as well in the future like push notifications and phone calls.

This module has been designed to abstract the way the notifications are sent and the differing content as much as possible
from the calling site.

For notifications that require templates, we are standardizing on Thymeleaf as our template engine.

### Components

#### Notification
This is the sealed class that provides the API for triggering rendering. Each sub-class handles the actual rendering of its content.
We are using a sealed class instead of an interface to force all implementations to be co-located.

#### Notification Factory
To avoid passing around a template engine instance to all the places that want to create a notification, we
are hiding it behind a factory class. Each notification sub-class should have a constructor method on the factory and it's only
constructor should be marked as `internal` to prevent direct construction.

#### Notification Manager
This is the entry point for sending notifications. It accepts an instance of `Notification` and contact info. It will decide
what medium should be used for communication - for example, if no phone number is available for SMS, it will use email. The
notification will be asked to render itself to produce a string.

Once the notification has been rendered, individual providers of communication mediums will be invoked to transmit the notification.
Loading