@@ -6,23 +6,23 @@ import javax.inject.{ Inject, Singleton }
6
6
import scala .collection .immutable
7
7
import play .api .{ Configuration , Logger }
8
8
import play .api .libs .json .JsObject
9
- import org .elastic4play .models .{ Attribute , AttributeDef , EntityDef , EnumerationAttributeFormat , ModelDef , MultiAttributeFormat , ObjectAttributeFormat , OptionalAttributeFormat , StringAttributeFormat , AttributeFormat ⇒ F , AttributeOption ⇒ O }
9
+ import org .elastic4play .models .{ Attribute , AttributeFormat , AttributeDef , EntityDef , EnumerationAttributeFormat , ListEnumeration , ModelDef , MultiAttributeFormat , ObjectAttributeFormat , OptionalAttributeFormat , StringAttributeFormat , AttributeOption ⇒ O }
10
10
import org .elastic4play .services .AuditableAction
11
11
import org .elastic4play .services .JsonFormat .auditableActionFormat
12
12
import services .AuditedModel
13
13
14
14
trait AuditAttributes { _ : AttributeDef ⇒
15
15
def detailsAttributes : Seq [Attribute [_]]
16
16
17
- val operation : A [AuditableAction .Value ] = attribute(" operation" , F .enumFmt(AuditableAction ), " Operation" , O .readonly)
18
- val details : A [JsObject ] = attribute(" details" , F .objectFmt(detailsAttributes), " Details" , JsObject (Nil ), O .readonly)
19
- val otherDetails : A [Option [String ]] = optionalAttribute(" otherDetails" , F .textFmt, " Other details" , O .readonly)
20
- val objectType : A [String ] = attribute(" objectType" , F .stringFmt, " Table affected by the operation" , O .readonly)
21
- val objectId : A [String ] = attribute(" objectId" , F .stringFmt, " Object targeted by the operation" , O .readonly)
22
- val base : A [Boolean ] = attribute(" base" , F .booleanFmt, " Indicates if this operation is the first done for a http query" , O .readonly)
23
- val startDate : A [Date ] = attribute(" startDate" , F .dateFmt, " Date and time of the operation" , new Date , O .readonly)
24
- val rootId : A [String ] = attribute(" rootId" , F .stringFmt, " Root element id (routing id)" , O .readonly)
25
- val requestId : A [String ] = attribute(" requestId" , F .stringFmt, " Id of the request that do the operation" , O .readonly)
17
+ val operation : A [AuditableAction .Value ] = attribute(" operation" , AttributeFormat .enumFmt(AuditableAction ), " Operation" , O .readonly)
18
+ val details : A [JsObject ] = attribute(" details" , AttributeFormat .objectFmt(detailsAttributes), " Details" , JsObject (Nil ), O .readonly)
19
+ val otherDetails : A [Option [String ]] = optionalAttribute(" otherDetails" , AttributeFormat .textFmt, " Other details" , O .readonly)
20
+ val objectType : A [String ] = attribute(" objectType" , AttributeFormat .stringFmt, " Table affected by the operation" , O .readonly)
21
+ val objectId : A [String ] = attribute(" objectId" , AttributeFormat .stringFmt, " Object targeted by the operation" , O .readonly)
22
+ val base : A [Boolean ] = attribute(" base" , AttributeFormat .booleanFmt, " Indicates if this operation is the first done for a http query" , O .readonly)
23
+ val startDate : A [Date ] = attribute(" startDate" , AttributeFormat .dateFmt, " Date and time of the operation" , new Date , O .readonly)
24
+ val rootId : A [String ] = attribute(" rootId" , AttributeFormat .stringFmt, " Root element id (routing id)" , O .readonly)
25
+ val requestId : A [String ] = attribute(" requestId" , AttributeFormat .stringFmt, " Id of the request that do the operation" , O .readonly)
26
26
}
27
27
28
28
@ Singleton
@@ -37,46 +37,64 @@ class AuditModel(
37
37
configuration.getString(" audit.name" ).get,
38
38
auditedModels)
39
39
40
- lazy val log = Logger (getClass)
40
+ lazy val logger = Logger (getClass)
41
41
42
- def detailsAttributes : Seq [Attribute [_]] = {
43
- auditedModels
44
- .flatMap(_.attributes)
45
- .flatMap {
46
- // if attribute is object, add sub attributes
47
- case attr @ Attribute (_, _, ObjectAttributeFormat (subAttributes), _, _, _) ⇒ attr +: subAttributes
48
- case attr ⇒ Seq (attr)
49
- }
50
- .filter(_.isModel)
51
- .map {
52
- case attr @ Attribute (_, _, OptionalAttributeFormat (_), _, _, _) ⇒ attr
53
- case attr @ Attribute (_, _, MultiAttributeFormat (_), _, _, _) ⇒ attr
54
- case attr ⇒ attr.toOptional
55
- }
42
+ def mergeAttributeFormat (context : String , format1 : AttributeFormat [_], format2 : AttributeFormat [_]): Option [AttributeFormat [_]] = {
43
+ (format1, format2) match {
44
+ case (OptionalAttributeFormat (f1), f2) ⇒ mergeAttributeFormat(context, f1, f2)
45
+ case (f1, OptionalAttributeFormat (f2)) ⇒ mergeAttributeFormat(context, f1, f2)
46
+ case (MultiAttributeFormat (f1), MultiAttributeFormat (f2)) ⇒ mergeAttributeFormat(context, f1, f2).map(MultiAttributeFormat (_))
47
+ case (f1, EnumerationAttributeFormat (_) | ListEnumeration (_)) ⇒ mergeAttributeFormat(context, f1, StringAttributeFormat )
48
+ case (EnumerationAttributeFormat (_) | ListEnumeration (_), f2) ⇒ mergeAttributeFormat(context, StringAttributeFormat , f2)
49
+ case (ObjectAttributeFormat (subAttributes1), ObjectAttributeFormat (subAttributes2)) ⇒ mergeAttributes(context, subAttributes1 ++ subAttributes2)
50
+ case (f1, f2) if f1 == f2 ⇒ Some (f1)
51
+ case (f1, f2) ⇒
52
+ logger.warn(s " $f1 != $f2" )
53
+ None
54
+
55
+ }
56
+ }
57
+
58
+ def mergeAttributes (context : String , attributes : Seq [Attribute [_]]): Option [ObjectAttributeFormat ] = {
59
+ val mergeAttributes : Iterable [Option [Attribute [_]]] = attributes
56
60
.groupBy(_.name)
57
- .flatMap {
58
- // if only one attribute is found for a name, get it
59
- case (_, attribute @ Seq (_)) ⇒ attribute
60
- // otherwise, check if attribute format is compatible
61
- case (_, attributes) ⇒
62
- attributes.headOption.foreach { first ⇒
63
- val isSensitive = first.isSensitive
64
- val formatName = first.format.name
65
- if (! attributes.forall(a ⇒ a.isSensitive == isSensitive && a.format.name == formatName)) {
66
- log.error(" Mapping is not consistent :" )
67
- attributes.foreach { attr ⇒
68
- val s = if (attr.isSensitive) " (is sensitive)" else " "
69
- log.error(s " \t ${attr.name} : ${attr.format.name} $s" )
70
- }
71
- }
72
- }
73
- attributes.headOption
74
- }
75
61
.map {
76
- case attr @ Attribute (_, _, EnumerationAttributeFormat (_), _, _, _) ⇒ attr.copy(format = StringAttributeFormat , defaultValue = None )
77
- case attr ⇒ attr
62
+ case (_name, _attributes) ⇒
63
+ _attributes
64
+ .map(a ⇒ Some (a.format))
65
+ .reduce[Option [AttributeFormat [_]]] {
66
+ case (Some (f1), Some (f2)) ⇒ mergeAttributeFormat(context + " ." + _name, f1, f2)
67
+ case _ ⇒ None
68
+ }
69
+ .map {
70
+ case oaf : OptionalAttributeFormat [_] ⇒ oaf : AttributeFormat [_]
71
+ case maf : MultiAttributeFormat [_] ⇒ maf : AttributeFormat [_]
72
+ case f ⇒ OptionalAttributeFormat (f): AttributeFormat [_]
73
+ }
74
+ .map(format ⇒ Attribute (" audit" , _name, format, Nil , None , " " ))
75
+ .orElse {
76
+ logger.error(s " Mapping is not consistent on attribute $context: \n ${_attributes.map(a ⇒ a.modelName + " /" + a.name + " : " + a.format.name).mkString(" \n " )}" )
77
+ None
78
+ }
78
79
}
79
- .toSeq
80
+
81
+ if (mergeAttributes.exists(_.isEmpty))
82
+ None
83
+ else
84
+ Some (ObjectAttributeFormat (mergeAttributes.flatten.toSeq))
85
+ }
86
+ def detailsAttributes : Seq [Attribute [_]] = {
87
+ mergeAttributes(" audit" , auditedModels
88
+ .flatMap(_.attributes)
89
+ .filter(a ⇒ a.isModel && ! a.isUnaudited)
90
+ .toSeq)
91
+ .map(_.subAttributes)
92
+ .getOrElse(Nil )
93
+ }
94
+
95
+ logger.info(" Audit attributes:" )
96
+ detailsAttributes.foreach { a ⇒
97
+ logger.info(s " \t ${a.name}: ${a.format}" )
80
98
}
81
99
override def apply (attributes : JsObject ): Audit = new Audit (this , attributes)
82
100
}
0 commit comments