forked from linagora/james-project
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CustomIdentityDAO.scala
125 lines (107 loc) · 6.17 KB
/
CustomIdentityDAO.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you 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 org.apache.james.jmap.api.identity
import java.nio.charset.StandardCharsets
import java.util.UUID
import com.google.common.collect.ImmutableList
import javax.inject.Inject
import org.apache.james.core.{MailAddress, Username}
import org.apache.james.jmap.api.model.{EmailAddress, HtmlSignature, Identity, IdentityId, IdentityName, MayDeleteIdentity, TextSignature}
import org.apache.james.rrt.api.CanSendFrom
import org.reactivestreams.Publisher
import reactor.core.scala.publisher.{SFlux, SMono}
import reactor.core.scheduler.Schedulers
import scala.jdk.CollectionConverters._
import scala.util.Try
case class IdentityCreationRequest(name: Option[IdentityName],
email: MailAddress,
replyTo: Option[List[EmailAddress]],
bcc: Option[List[EmailAddress]],
textSignature: Option[TextSignature],
htmlSignature: Option[HtmlSignature]) {
def asIdentity(id: IdentityId): Identity = Identity(id, name.getOrElse(IdentityName.DEFAULT), email, replyTo, bcc, textSignature.getOrElse(TextSignature.DEFAULT), htmlSignature.getOrElse(HtmlSignature.DEFAULT), mayDelete = MayDeleteIdentity(true))
}
trait IdentityUpdate {
def update(identity: Identity): Identity
}
case class IdentityNameUpdate(name: IdentityName) extends IdentityUpdate {
override def update(identity: Identity): Identity = identity.copy(name = name)
}
case class IdentityReplyToUpdate(replyTo: Option[List[EmailAddress]]) extends IdentityUpdate {
override def update(identity: Identity): Identity = identity.copy(replyTo = replyTo)
}
case class IdentityBccUpdate(bcc: Option[List[EmailAddress]]) extends IdentityUpdate {
override def update(identity: Identity): Identity = identity.copy(bcc = bcc)
}
case class IdentityTextSignatureUpdate(textSignature: TextSignature) extends IdentityUpdate {
override def update(identity: Identity): Identity = identity.copy(textSignature = textSignature)
}
case class IdentityHtmlSignatureUpdate(htmlSignature: HtmlSignature) extends IdentityUpdate {
override def update(identity: Identity): Identity = identity.copy(htmlSignature = htmlSignature)
}
case class IdentityUpdateRequest(name: Option[IdentityNameUpdate],
replyTo: Option[IdentityReplyToUpdate],
bcc: Option[IdentityBccUpdate],
textSignature: Option[IdentityTextSignatureUpdate],
htmlSignature: Option[IdentityHtmlSignatureUpdate]) extends IdentityUpdate {
def update(identity: Identity): Identity =
List(name, replyTo, bcc, textSignature, htmlSignature)
.flatten
.foldLeft(identity)((acc, update) => update.update(acc))
}
trait CustomIdentityDAO {
def save(user: Username, creationRequest: IdentityCreationRequest): Publisher[Identity]
def list(user: Username): Publisher[Identity]
def update(user: Username, identityId: IdentityId, identityUpdate: IdentityUpdate): Publisher[Unit]
def delete(username: Username, ids: Seq[IdentityId]): Publisher[Unit]
}
class DefaultIdentitySupplier @Inject()(canSendFrom: CanSendFrom) {
def listIdentities(username: Username): List[Identity] =
canSendFrom.allValidFromAddressesForUser(username)
.collect(ImmutableList.toImmutableList()).asScala.toList
.flatMap(address =>
from(address).map(id =>
Identity(
id = id,
name = IdentityName(address.asString()),
email = address,
replyTo = None,
bcc = None,
textSignature = TextSignature.DEFAULT,
htmlSignature = HtmlSignature.DEFAULT,
mayDelete = MayDeleteIdentity(false))))
private def from(address: MailAddress): Option[IdentityId] =
Try(UUID.nameUUIDFromBytes(address.asString().getBytes(StandardCharsets.UTF_8)))
.toEither
.toOption
.map(IdentityId(_))
}
// This class is intended to merge default (server-set0 identities with (user defined) custom identities
// Using the custom identities we can stores deltas of the default (server-set) identities allowing to modify them.
class IdentityRepository @Inject()(customIdentityDao: CustomIdentityDAO, identityFactory: DefaultIdentitySupplier) {
def save(user: Username, creationRequest: IdentityCreationRequest): Publisher[Identity] = customIdentityDao.save(user, creationRequest)
def list(user: Username): Publisher[Identity] = SFlux.merge(Seq(
customIdentityDao.list(user),
SMono.fromCallable(() => identityFactory.listIdentities(user))
.subscribeOn(Schedulers.elastic())
.flatMapMany(SFlux.fromIterable)))
def update(user: Username, identityId: IdentityId, identityUpdate: IdentityUpdate): Publisher[Unit] = customIdentityDao.update(user, identityId, identityUpdate)
def delete(username: Username, ids: Seq[IdentityId]): Publisher[Unit] = customIdentityDao.delete(username, ids)
}
case class IdentityNotFoundException(id: IdentityId) extends RuntimeException(s"$id could not be found")