cc156da3ef24701dafcb568e901fa258bb1bd4f7
[aquarium] / src / main / scala / gr / grnet / aquarium / computation / reason / UserStateChangeReason.scala
1 /*
2  * Copyright 2011-2012 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.aquarium.computation.reason
37
38 import gr.grnet.aquarium.computation.BillingMonthInfo
39 import gr.grnet.aquarium.event.model.im.IMEventModel
40 import gr.grnet.aquarium.util.shortClassNameOf
41
42 /**
43  * Provides information explaining the reason Aquarium calculated a new [[gr.grnet.aquarium.computation.UserState]].
44  */
45 case class UserStateChangeReason(
46     parentReason: Option[UserStateChangeReason],
47     billingMonthInfo: Option[BillingMonthInfo],
48     details: Map[String, Any]
49 ) {
50
51   require(
52     details.contains(UserStateChangeReason.Names.`type`),
53     "No type present in the details of %s".format(shortClassNameOf(this))
54   )
55
56   private[this] def booleanFromDetails(name: String, default: Boolean) = {
57     details.get(name) match {
58       case Some(value: Boolean) ⇒
59         value
60
61       case _ ⇒
62         false
63     }
64   }
65
66    /**
67     * Return `true` if the result of the calculation should be stored back to the
68     * [[gr.grnet.aquarium.store.UserStateStore]].
69     *
70     */
71   def shouldStoreUserState: Boolean =
72      booleanFromDetails(UserStateChangeReason.Names.shouldStoreUserState, false)
73
74   def shouldStoreCalculatedWalletEntries: Boolean =
75     booleanFromDetails(UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries, false)
76
77   def calculateCreditsForImplicitlyTerminated: Boolean =
78     booleanFromDetails(UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated, false)
79
80   def forBillingMonthInfo(bmi: BillingMonthInfo) = {
81     copy(
82       parentReason = Some(this),
83       billingMonthInfo = Some(bmi)
84     )
85   }
86
87   def `type`: String = {
88     // This must be always present
89     details(UserStateChangeReason.Names.`type`).asInstanceOf[String]
90   }
91 }
92
93 object UserStateChangeReason {
94   object Names {
95     final val `type` = "type"
96
97     final val imEvent = "imEvent"
98     final val forWhenMillis = "forWhenMillis"
99
100     final val shouldStoreUserState = "shouldStoreUserState"
101     final val shouldStoreCalculatedWalletEntries = "shouldStoreCalculatedWalletEntries"
102     final val calculateCreditsForImplicitlyTerminated = "calculateCreditsForImplicitlyTerminated"
103   }
104 }
105
106 sealed trait UserStateChangeReason_ {
107   def originalReason: UserStateChangeReason_
108   /**
109     * Return `true` if the result of the calculation should be stored back to the
110     * [[gr.grnet.aquarium.store.UserStateStore]].
111     *
112     */
113   def shouldStoreUserState: Boolean
114
115   def shouldStoreCalculatedWalletEntries: Boolean
116
117   def forPreviousBillingMonth: UserStateChangeReason_
118
119   def calculateCreditsForImplicitlyTerminated: Boolean
120
121   def code: UserStateChangeReasonCodes.ChangeReasonCode
122 }
123
124 object InitialUserStateSetup {
125   def `type` = "InitialUserStateSetup"
126
127   /**
128    * When the user state is initially set up.
129    */
130   def apply(parentReason: Option[UserStateChangeReason]) = {
131     UserStateChangeReason(
132       parentReason,
133       None,
134       Map(
135         UserStateChangeReason.Names.`type` -> `type`,
136         UserStateChangeReason.Names.shouldStoreUserState -> true
137       )
138     )
139   }
140 }
141
142 object InitialUserActorSetup {
143   def `type` = "InitialUserActorSetup"
144
145   /**
146    * When the user processing unit (actor) is initially set up.
147    */
148   def apply() = {
149     UserStateChangeReason(
150       None,
151       None,
152       Map(
153         UserStateChangeReason.Names.`type` -> `type`,
154         UserStateChangeReason.Names.shouldStoreUserState -> true
155       )
156     )
157   }
158 }
159
160 object NoSpecificChangeReason {
161   def `type` = "NoSpecificChangeReason"
162
163   /**
164    * A calculation made for no specific reason. Can be for testing, for example.
165    */
166   def apply() = {
167     UserStateChangeReason(
168       None,
169       None,
170       Map(
171         UserStateChangeReason.Names.`type` -> `type`
172       )
173     )
174   }
175 }
176
177 object MonthlyBillingCalculation {
178   def `type` = "MonthlyBillingCalculation"
179
180   /**
181    * An authoritative calculation for the billing period.
182    */
183   def apply(parentReason: UserStateChangeReason, billingMongthInfo: BillingMonthInfo) = {
184     UserStateChangeReason(
185       Some(parentReason),
186       Some(billingMongthInfo),
187       Map(
188         UserStateChangeReason.Names.`type` -> `type`,
189         UserStateChangeReason.Names.shouldStoreUserState -> true,
190         UserStateChangeReason.Names.shouldStoreCalculatedWalletEntries -> true,
191         UserStateChangeReason.Names.calculateCreditsForImplicitlyTerminated -> true
192       )
193     )
194   }
195 }
196
197 object RealtimeBillingCalculation {
198   def `type` = "RealtimeBillingCalculation"
199
200   /**
201    * Used for the real-time billing calculation.
202    */
203   def apply(parentReason: Option[UserStateChangeReason], forWhenMillis: Long) = {
204     UserStateChangeReason(
205       parentReason,
206       None,
207       Map(
208         UserStateChangeReason.Names.`type` -> `type`,
209         UserStateChangeReason.Names.forWhenMillis -> forWhenMillis
210       )
211     )
212   }
213 }
214
215 object IMEventArrival {
216   def `type` = "IMEventArrival"
217
218   def apply(imEvent: IMEventModel) = {
219     UserStateChangeReason(
220       None,
221       None,
222       Map(
223         UserStateChangeReason.Names.`type` -> `type`,
224         UserStateChangeReason.Names.imEvent -> imEvent,
225         UserStateChangeReason.Names.shouldStoreUserState -> true
226       )
227     )
228   }
229 }