aboutsummaryrefslogblamecommitdiff
path: root/README.md
blob: 952961f3b6578157e7f39f57f5972fe8a9f544de (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                                                                                                                                  
 
                                                                                                                                                                                                                                                  
 
             
 
                                                                                                                                                        
                                                                                                        
                                                                           
                                                                                                 
                                                                                                                                                                                                    




                                                                                                                                                         
                                                                                              


                                                                                     
 
                                                                                                                                                                                                                                                        
 
           
 
                                
                                                                                                                                                                                                                                                                                                               






















                                                                 

























                                                                                        


                             
                                                                                                                                                                          




























                                                                            


























































                                                                                                                                                                                                               










                                                                     
                                 

                     
# Driver Core Library [![Build Status](https://travis-ci.com/drivergroup/driver-core.svg?token=S4oyfBY3YoEdLmckujJx&branch=master)](https://travis-ci.com/drivergroup/driver-core)

Core library is used to provide ways to implement practices established in [Driver service template](http://github.com/drivergroup/driver-template) (check its [README.md](https://github.com/drivergroup/driver-template/blob/master/README.md)).

## Components

 * `core package` provides `Id` and `Name` implementations (with equal and ordering), utils for ScalaZ `OptionT`, and also `make` and `using` functions,
 * `config` Contains method `loadDefaultConfig` with default way of providing config to the application,
 * `domain` Common generic domain objects, e.g., `Email` and `PhoneNumber`,
 * `messages` Localization messages supporting different locales and methods to read from config,
 * `database` Method for database initialization from config, `Id`, `Name`, `Time`, `Date` etc. mapping, schema lifecycle and base classes to implement and test `Repository` (data access objects),
 * `rest` Wrapper over call to external REST API, authorization, context headers, XSS protection, does logging and allows to add Swagger to a service,
 * `auth` Basic entities for authentication and authorization: `User`, `Role` `Permission` `AuthToken`, `AuthCredentials` etc.,
 * `swagger` Contains custom `AbstractModelConverter` to customize Swagger JSON with any Scala  JSON formats created by, for instance, Spray JSON,
 * `json` Json formats for `Id`, `Name`, `Time`, `Revision`, `Email`, `PhoneNumber`, `AuthCredentials` and converters for GADTs, enums and value classes,
 * `file` Interface `FileStorage` and file storage implementations with GCS, S3 and local FS,
 * `app` Base class for Driver service, which initializes swagger, app modules and its routes.
 * `generators` Set of functions to prototype APIs. Combines with `faker` package,
 * `stats` Methods to collect system stats: memory, cpu, gc, file system space usage,
 * `logging` Custom Driver logging layout (not finished yet).

Dependencies of core modules might be found in [Dependencies of the Modules diagram](https://github.com/drivergroup/driver-template/blob/master/Modules%20dependencies.pdf) file in driver-template repository in "Core component dependencies" section.

## Examples

### Functions `make` and `using`
Those functions are especially useful to make procedural legacy Java APIs more functional and make scope of its usage more explicit. Runnable examples of its usage might be found in [`CoreTest`](https://github.com/drivergroup/driver-core/blob/master/src/test/scala/xyz/driver/core/CoreTest.scala), e.g.,

    useObject(make(new ObjectWithProceduralInitialization) { o =>
      o.setSetting1(...) // returns Unit
      o.setSetting2(...) // returns Unit
      o.setSetting3(...) // returns Unit
    })

    // instead of
    val o = new ObjectWithProceduralInitialization
    o.setSetting1(...)
    o.setSetting2(...)
    o.setSetting3(...)

    useObject(o)

and

    using(... open output stream ...) { os =>
      os.write(...)
    }

    // it will be close automatically

### `OptionT` utils
Before

```
OptionT.optionT[Future](service.getRecords(id).map(Option.apply))
OptionT.optionT(service.doSomething(id).map(_ => Option(())))

// Do not want to stop and return `None`, if `produceEffect` returns `None`
for {
  x <- service.doSomething(id)
  _ <- service.produceEffect(id, x).map(_ => ()).orElse(OptionT.some[Future, Unit](())))
} yield x
```

after

```
service.getRecords(id).toOptionT
service.doSomething(id).toUnitOptionT

// Do not want to stop and return `None` if `produceEffect` returns `None`
for {
  x <- service.doSomething(id)
  _ <- service.produceEffect(id, x).continueIgnoringNone
} yield x
```

### `Time` and `TimeProvider`

Usage examples for `Time` (also check [TimeTest](https://github.com/drivergroup/driver-core/blob/master/src/test/scala/xyz/driver/core/TimeTest.scala) for more examples).

    Time(234L).isAfter(Time(123L))

    Time(123L).isBefore(Time(234L))

    Time(123L).advanceBy(4 days)

    Seq(Time(321L), Time(123L), Time(231L)).sorted

    startOfMonth(Time(1468937089834L)) should be (Time(1467381889834L))

    textualDate(Time(1468937089834L)) should be ("July 19, 2016")

    textualTime(Time(1468937089834L)) should be ("Jul 19, 2016 10:04:49 AM")

    TimeRange(Time(321L), Time(432L)).duration should be(111.milliseconds)


### Generators
Example of how to generate a case class instance,

    import com.drivergrp.core._

    Consumer(
         generators.nextId[Consumer],
         Name[Consumer](faker.Name.name),
         faker.Lorem.sentence(word_count = 10))


For more examples check [project tests](https://github.com/drivergroup/driver-core/tree/master/src/test/scala/xyz/driver/core) or [service template](http://github.com/drivergroup/driver-template) repository.

### App

To start a new application using standard Driver application class, follow this pattern:

    object MyApp extends App {

      new DriverApp(BuildInfo.version,
                    BuildInfo.gitHeadCommit.getOrElse("None"),
                    modules = Seq(myModule1, myModule2),
                    time, log, config,
                    interface = "::0", baseUrl, scheme, port)
                   (servicesActorSystem, servicesExecutionContext).run()
    }

### REST
With REST utils, for instance, you can use the following directives in [akka-http](https://github.com/akka/akka-http) routes, as follows

    sanitizeRequestEntity { // Prevents XSS
      serviceContext { implicit ctx => // Extracts context headers from request
        authorize(CanSeeUser(userId)) { user => // Authorizes and extracts user
          // Your code using `ctx` and `user`
        }
      }
    }

### Swagger
Swagger JSON formats built using reflection can be overriden by using `CustomSwaggerJsonConverter` at the start of your application initialization in the following way:

    ModelConverters.getInstance()
      .addConverter(new CustomSwaggerJsonConverter(Json.mapper(),
         CustomSwaggerFormats.customProperties, CustomSwaggerFormats.customObjectsExamples))

### Locale
Locale messages can be initialized and used in the following way,

    val localeConfig = config.getConfig("locale")
    val log = com.typesafe.scalalogging.Logger(LoggerFactory.getLogger(classOf[MyClass]))

    val messages = xyz.driver.core.messages.Messages.messages(localeConfig, log, Locale.US)

    messages("my.locale.message")
    messages("my.locale.message.with.param", parameter)


### System stats
Stats it gives access to are,

    xyz.driver.core.stats.SystemStats.memoryUsage

    xyz.driver.core.stats.SystemStats.availableProcessors

    xyz.driver.core.stats.SystemStats.garbageCollectorStats

    xyz.driver.core.stats.SystemStats.fileSystemSpace

    xyz.driver.core.stats.SystemStats.operatingSystemStats


## Running

1. Compile everything and test:

        $ sbt test

2. Publish to local repository, to use changes in depending projects:

        $ sbt publish-local

3. Release a new version of core:

        $ sbt release