Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crud #6

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
209 changes: 198 additions & 11 deletions src/main/scala/ru/otus/sc/App.scala
Original file line number Diff line number Diff line change
@@ -1,22 +1,209 @@
package ru.otus.sc

import ru.otus.sc.greet.dao.impl.GreetingDaoImpl
import ru.otus.sc.Engine.{Greeted, StorageKey, StorageValue}
import ru.otus.sc.countdown.model._
import ru.otus.sc.counter.model._
import ru.otus.sc.echo.model.{EchoRequest, EchoResponse}
import ru.otus.sc.greet.model.{GreetRequest, GreetResponse}
import ru.otus.sc.greet.service.GreetingService
import ru.otus.sc.greet.service.impl.GreetingServiceImpl
import ru.otus.sc.reverse.model.{ReverseRequest, ReverseResponse}
import ru.otus.sc.storage.model._
import ru.otus.sc.sum.model.{SumRequest, SumResponse}
import ru.otus.sc.user.model._

trait App {
def greet(request: GreetRequest): GreetResponse
// reply on requested value with the same value
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Упоминал в прошлом ДЗ что лучше использовать ScalaDoc формат комментариев.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, я делал его до проверки прошлого ДЗ, поэтому все еще так :))

// echo value can be multiplied up to 5 times with `repeatNum` value
// no multiply answer by default (`repeatNum` is 1)
// proper values return answer with `EchoAnswerResponse`
// bad values return error with `EchoErrorResponse`
def echo(request: EchoRequest): EchoResponse
// greet with provided object
// panic if `isHuman` flag is unset
// greeting request requires implicit parameter for extracting name from the object
// in current implementation object is User and implicit can be borrowed from
// `ru.otus.sc.user.implicits.UserNameImplicits._`
def greet(request: GreetRequest[Greeted]): GreetResponse
// reverse given string from end to begin
def reverse(request: ReverseRequest): ReverseResponse
// sum 2 given values locally
// sum can be performed externally with `external` flag
// in current implementation external computation is artificial delay
// which was implemented with lazy value
def sum(request: SumRequest): SumResponse
// manage users
// user is compound from:
// id, which is DB related stuff like pk
// unique id, UUID, this item is unique for whole system
// user name, which is compound from: first, last, middle, patronymic names and title
// age
// set of tags
// CRUD operations are available as well as search for first, last name and tag
def createUser(request: CreateUserRequest): CreateUserResponse
def getUser(request: GetUserRequest): GetUserResponse
def deleteUser(request: DeleteUserRequest): DeleteUserResponse
def updateUser(request: UpdateUserRequest): UpdateUserResponse
def findUsers(request: FindUsersRequest): FindUsersResponse
// manage user tags
// tags are bound with users and related only them
// tag is compound from:
// id, which is DB related stuff like pk
// name, for human
// CRUD operations are available as well as search for name
// also associated with certain tag users can be obtained in search request
// tags and users are totally independent
// tagging and untagging users do not allow manage tags on users directly
// and have to be performed as separate operations for users and tags
// e.g. tagging user example,
// imagine we already have app, user, tag and all operations are done successfully:
// val tagId = 42L
// val uniqueUserId = UUID.fromString("4a71a58b-4b39-44fc-ae52-76b9657be280")
// val tag = app.getUserTag(GetUserTagRequest(tagId))
// val user = app.getUser(GetUserRequest(uniqueUserId))
// val updatedUser = user.copy(tags = (user.tags + tag))
// app.tagUser(UpdateTagUserRequest(tagId, uniqueUserId))
// app.updateUser(UpdateUserRequest(updatedUser))
def createUserTag(request: CreateUserTagRequest): CreateUserTagResponse
def getUserTag(request: GetUserTagRequest): GetUserTagResponse
def deleteUserTag(request: DeleteUserTagRequest): DeleteUserTagResponse
def updateUserTag(request: UpdateUserTagRequest): UpdateUserTagResponse
def findUserTags(request: FindUserTagsRequest): FindUserTagsResponse
def tagUser(request: UpdateTagUserRequest): UpdateTagUserResponse
def untagUser(request: UpdateUntagUserRequest): UpdateUntagUserResponse
// manage counters
// counter is auto-increment item with initial `value`, default value is 1
// update increments counter by 1
// counter value can not be updated itself, it can be only incremented
// timestamp is updated on every counter update
// counter is compound from:
// id, UUID, this item is unique for whole system
// timestamp of update
// value of counter
// CRUD operations are available as well as search for values or timestamps
// searching requires predicate (t: T, i: T) => Boolean for comparing items
// where `t` is target and `i` is item in DB
def createCounter(request: CreateCounterRequest): CreateCounterResponse
def deleteCounter(request: DeleteCounterRequest): DeleteCounterResponse
def updateCounter(request: UpdateCounterRequest): UpdateCounterResponse
def getCounter(request: GetCounterRequest): GetCounterResponse
def findCounters(request: FindCountersRequest): FindCountersResponse
// manage countdowns
// countdown is auto-decrement item with initial `value`, default value is 1
// update auto-decreases value by 1
// when countdown reaches 0 it stops decrease value and swaps state from Tick to Done
// Countdown.Done is composed from id, UUID, this item is unique for whole system
// State above can not be updated anymore, also it can be created `CountdownDone`
// Countdown.Tick is composed from:
// id, UUID, this item is unique for whole system
// updater, UUID this is id of something that create or update countdown
// value of countdown
// CRUD operations are available as well as search for:
// values, updaters, Done and NonDone states
// searching for values requires predicate (t: T, i: T) => Boolean for comparing items
// where `t` is target and `i` is item in DB
def createCountdown(request: CreateCountdownRequest): CreateCountdownResponse
def deleteCountdown(request: DeleteCountdownRequest): DeleteCountdownResponse
def updateCountdown(request: UpdateCountdownRequest): UpdateCountdownResponse
def getCountdown(request: GetCountdownRequest): GetCountdownResponse
def findCountdowns(request: FindCountdownsRequest): FindCountdownsResponse
// manage storage
// provide value by requested key
// StorageEntry is composed from key and value of defined types
// in current implementation keys and values are String
// CRUD operations are available as well as search for value itself or predicate on value
// searching with predicate requires predicate i: V => Boolean for comparing items
// where `i` is item in DB
def createStorage(
request: CreateStorageRequest[StorageKey, StorageValue]
): CreateStorageResponse[StorageKey, StorageValue]
def getStorage(
request: GetStorageRequest[StorageKey, StorageValue]
): GetStorageResponse[StorageKey, StorageValue]
def deleteStorage(
request: DeleteStorageRequest[StorageKey, StorageValue]
): DeleteStorageResponse[StorageKey, StorageValue]
def updateStorage(
request: UpdateStorageRequest[StorageKey, StorageValue]
): UpdateStorageResponse[StorageKey, StorageValue]
def findStorages(
request: FindStoragesRequest[StorageKey, StorageValue]
): FindStoragesResponse[StorageKey, StorageValue]
}

object App {
private class AppImpl(greeting: GreetingService) extends App {
def greet(request: GreetRequest): GreetResponse = greeting.greet(request)
}
def apply(): App = new AppImpl(Engine())

def apply(): App = {
val greetingDao = new GreetingDaoImpl
val greetingService = new GreetingServiceImpl(greetingDao)
new AppImpl(greetingService)
private class AppImpl(engine: Engine) extends App {
def echo(request: EchoRequest): EchoResponse =
engine.echoing.echo(request)
def greet(request: GreetRequest[Greeted]): GreetResponse =
engine.greeting.greet(request)
def reverse(request: ReverseRequest): ReverseResponse =
engine.reversing.reverse(request)
def sum(request: SumRequest): SumResponse =
engine.summing.sum(request)
def createUser(request: CreateUserRequest): CreateUserResponse =
engine.usering.createUser(request)
def getUser(request: GetUserRequest): GetUserResponse =
engine.usering.getUser(request)
def deleteUser(request: DeleteUserRequest): DeleteUserResponse =
engine.usering.deleteUser(request)
def updateUser(request: UpdateUserRequest): UpdateUserResponse =
engine.usering.updateUser(request)
def findUsers(request: FindUsersRequest): FindUsersResponse =
engine.usering.findUsers(request)
def createUserTag(request: CreateUserTagRequest): CreateUserTagResponse =
engine.userTagging.createUserTag(request)
def getUserTag(request: GetUserTagRequest): GetUserTagResponse =
engine.userTagging.getUserTag(request)
def deleteUserTag(request: DeleteUserTagRequest): DeleteUserTagResponse =
engine.userTagging.deleteUserTag(request)
def updateUserTag(request: UpdateUserTagRequest): UpdateUserTagResponse =
engine.userTagging.updateUserTag(request)
def findUserTags(request: FindUserTagsRequest): FindUserTagsResponse =
engine.userTagging.findUserTags(request)
def tagUser(request: UpdateTagUserRequest): UpdateTagUserResponse =
engine.userTagging.tagUser(request)
def untagUser(request: UpdateUntagUserRequest): UpdateUntagUserResponse =
engine.userTagging.untagUser(request)
def createCounter(request: CreateCounterRequest): CreateCounterResponse =
engine.counting.createCounter(request)
def deleteCounter(request: DeleteCounterRequest): DeleteCounterResponse =
engine.counting.deleteCounter(request)
def updateCounter(request: UpdateCounterRequest): UpdateCounterResponse =
engine.counting.updateCounter(request)
def getCounter(request: GetCounterRequest): GetCounterResponse =
engine.counting.getCounter(request)
def findCounters(request: FindCountersRequest): FindCountersResponse =
engine.counting.findCounters(request)
def createCountdown(request: CreateCountdownRequest): CreateCountdownResponse =
engine.countdowning.createCountdown(request)
def deleteCountdown(request: DeleteCountdownRequest): DeleteCountdownResponse =
engine.countdowning.deleteCountdown(request)
def updateCountdown(request: UpdateCountdownRequest): UpdateCountdownResponse =
engine.countdowning.updateCountdown(request)
def getCountdown(request: GetCountdownRequest): GetCountdownResponse =
engine.countdowning.getCountdown(request)
def findCountdowns(request: FindCountdownsRequest): FindCountdownsResponse =
engine.countdowning.findCountdowns(request)
def createStorage(
request: CreateStorageRequest[StorageKey, StorageValue]
): CreateStorageResponse[StorageKey, StorageValue] =
engine.getting.createStorage(request)
def deleteStorage(
request: DeleteStorageRequest[StorageKey, StorageValue]
): DeleteStorageResponse[StorageKey, StorageValue] =
engine.getting.deleteStorage(request)
def updateStorage(
request: UpdateStorageRequest[StorageKey, StorageValue]
): UpdateStorageResponse[StorageKey, StorageValue] =
engine.getting.updateStorage(request)
def getStorage(
request: GetStorageRequest[StorageKey, StorageValue]
): GetStorageResponse[StorageKey, StorageValue] =
engine.getting.getStorage(request)
def findStorages(
request: FindStoragesRequest[StorageKey, StorageValue]
): FindStoragesResponse[StorageKey, StorageValue] =
engine.getting.findStorages(request)
}
}
80 changes: 80 additions & 0 deletions src/main/scala/ru/otus/sc/Engine.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package ru.otus.sc

import ru.otus.sc.Engine.{Greeted, StorageKey, StorageValue}
import ru.otus.sc.countdown.dao.impl.CountdownDaoImpl
import ru.otus.sc.countdown.service.CountdownService
import ru.otus.sc.countdown.service.impl.CountdownServiceImpl
import ru.otus.sc.counter.dao.impl.CounterDaoImpl
import ru.otus.sc.counter.service.CounterService
import ru.otus.sc.counter.service.impl.CounterServiceImpl
import ru.otus.sc.echo.dao.impl.EchoDaoImpl
import ru.otus.sc.echo.service.EchoService
import ru.otus.sc.echo.service.impl.EchoServiceImpl
import ru.otus.sc.greet.dao.impl.GreetingDaoImpl
import ru.otus.sc.greet.service.GreetingService
import ru.otus.sc.greet.service.impl.GreetingServiceImpl
import ru.otus.sc.reverse.dao.impl.ReverseDaoImpl
import ru.otus.sc.reverse.service.ReverseService
import ru.otus.sc.reverse.service.impl.ReverseServiceImpl
import ru.otus.sc.storage.dao.impl.StorageDaoImpl
import ru.otus.sc.storage.service.StorageService
import ru.otus.sc.storage.service.impl.StorageServiceImpl
import ru.otus.sc.sum.dao.impl.SumDaoImpl
import ru.otus.sc.sum.service.SumService
import ru.otus.sc.sum.service.impl.SumServiceImpl
import ru.otus.sc.user.dao.impl.{UserDaoImpl, UserTagDaoImpl}
import ru.otus.sc.user.model.User
import ru.otus.sc.user.service.impl.{UserServiceImpl, UserTagServiceImpl}
import ru.otus.sc.user.service.{UserService, UserTagService}

// Helper class which aggregates service entries to single point
case class Engine(
countdowning: CountdownService,
counting: CounterService,
echoing: EchoService,
getting: StorageService[StorageKey, StorageValue],
greeting: GreetingService[Greeted],
reversing: ReverseService,
summing: SumService,
usering: UserService,
userTagging: UserTagService
)

object Engine {
type StorageKey = String
type StorageValue = String
type Greeted = User

def apply(): Engine = {
val CountdownDao = new CountdownDaoImpl
val countdownService = new CountdownServiceImpl(CountdownDao)
val CounterDao = new CounterDaoImpl
val counterService = new CounterServiceImpl(CounterDao)
val echoDao = new EchoDaoImpl
val echoService = new EchoServiceImpl(echoDao)
val greetingDao = new GreetingDaoImpl
val greetingService = new GreetingServiceImpl[Greeted](greetingDao)
val storageDao = new StorageDaoImpl[StorageKey, StorageValue]
val storageService = new StorageServiceImpl[StorageKey, StorageValue](storageDao)
val reverseDao = new ReverseDaoImpl
val reverseService = new ReverseServiceImpl(reverseDao)
val sumDao = new SumDaoImpl
val sumService = new SumServiceImpl(sumDao)
val userDao = new UserDaoImpl
val userService = new UserServiceImpl(userDao)
val userTagDao = new UserTagDaoImpl
val userTagService = new UserTagServiceImpl(userTagDao)

Engine(
countdownService,
counterService,
echoService,
storageService,
greetingService,
reverseService,
sumService,
userService,
userTagService
)
}
}
15 changes: 15 additions & 0 deletions src/main/scala/ru/otus/sc/countdown/dao/CountdownDao.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.otus.sc.countdown.dao

import ru.otus.sc.countdown.model.Countdown
import ru.otus.sc.countdown.model.Countdown.{CompareValues, CountdownId, CountdownValue, UpdaterId}

trait CountdownDao {
def createCountdown(countdown: Countdown): Countdown
def deleteCountdown(id: CountdownId): Option[Countdown]
def getCountdown(id: CountdownId): Option[Countdown]
def updateCountdown(countdown: Countdown): Option[Countdown]
def findCountdownsByValue(value: CountdownValue, predicate: CompareValues): Seq[Countdown]
def findCountdownsByUpdater(updater: UpdaterId): Seq[Countdown]
def findAllDone: Seq[Countdown]
def findAllNonDone: Seq[Countdown]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package ru.otus.sc.countdown.dao.impl

import java.util.UUID

import ru.otus.sc.countdown.dao.CountdownDao
import ru.otus.sc.countdown.model.Countdown
import ru.otus.sc.countdown.model.Countdown.{CompareValues, CountdownId, CountdownValue, UpdaterId}

class CountdownDaoImpl extends CountdownDao {
private var countdowns = Map[CountdownId, Countdown]()

def createCountdown(countdown: Countdown): Countdown = {

val id = UUID.randomUUID()
val newCountdown = countdown match {
case Countdown.Done(_) => Countdown.Done(Some(id))
case Countdown.Tick(_, updater, value) => Countdown.Tick(Some(id), updater, value)
}
countdowns += (id -> newCountdown)
newCountdown
}

def deleteCountdown(id: CountdownId): Option[Countdown] =
for {
deletedCountdown <- countdowns.get(id)
} yield {
countdowns -= id
deletedCountdown
}

def getCountdown(id: CountdownId): Option[Countdown] = countdowns.get(id)

def updateCountdown(countdown: Countdown): Option[Countdown] = {
// only CountdownTick can be updated
val (countdownId, updaterId) = countdown match {
case Countdown.Tick(id, updater, _) => (id, Some(updater))
case Countdown.Done(_) => (None, None)
}

for {
id <- countdownId
updater <- updaterId
currentCountdown <- countdowns.get(id)
} yield {
val newCountdown = currentCountdown match {
case alreadyDone @ Countdown.Done(_) => alreadyDone
case Countdown.Tick(id, _, value) =>
val newValue = value - 1L
if (newValue <= 0) Countdown.Done(id)
else Countdown.Tick(id, updater, newValue)
}
countdowns += (id -> newCountdown)
newCountdown
}
}

def findCountdownsByValue(value: CountdownValue, predicate: CompareValues): Seq[Countdown] =
countdowns.values.collect {
case x @ Countdown.Tick(_, _, currentValue) if predicate(value, currentValue) => x
}.toVector

def findCountdownsByUpdater(updater: UpdaterId): Seq[Countdown] =
countdowns.values.collect {
case x @ Countdown.Tick(_, updaterId, _) if updaterId == updater => x
}.toVector

def findAllDone: Seq[Countdown] =
countdowns.values.collect { case x @ Countdown.Done(_) => x }.toVector

def findAllNonDone: Seq[Countdown] =
countdowns.values.collect { case x @ Countdown.Tick(_, _, _) => x }.toVector
}
Loading