dregs 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/dregs/models.rb CHANGED
@@ -64,6 +64,12 @@ module Dregs
64
64
  def array_or_empty(value)
65
65
  value.is_a?(Array) ? value : []
66
66
  end
67
+
68
+ # @param value [Object]
69
+ # @return [Integer] the value when it is an integer, otherwise 0
70
+ def integer_or_zero(value)
71
+ value.is_a?(Integer) ? value : 0
72
+ end
67
73
  end
68
74
 
69
75
  # The outcome of a {Client#track} call.
@@ -517,4 +523,668 @@ module Dregs
517
523
  )
518
524
  end
519
525
  end
526
+
527
+ # One page of a list call.
528
+ #
529
+ # Enumerable over the items on this page. Pages are numbered from 0. To walk every page, use the
530
+ # namespace's +list_all+ rather than looping on +page_number+ yourself.
531
+ #
532
+ # page = client.identities.list(term: "example.com")
533
+ #
534
+ # page.map(&:id)
535
+ # page.has_next? #=> whether another page follows
536
+ #
537
+ # @!attribute [r] page_number
538
+ # @return [Integer] this page's number, counting from 0
539
+ # @!attribute [r] page_size
540
+ # @return [Integer] the most items a page holds
541
+ # @!attribute [r] total_pages
542
+ # @return [Integer] how many pages there are in all
543
+ # @!attribute [r] total_elements
544
+ # @return [Integer] how many items there are across every page
545
+ # @!attribute [r] raw
546
+ # @return [Hash] the page as received
547
+ class Page
548
+ include Enumerable
549
+
550
+ # @return [Array] the items on this page
551
+ attr_reader :to_a
552
+
553
+ attr_reader :page_number, :page_size, :total_pages, :total_elements, :raw
554
+
555
+ # @api private
556
+ def initialize(attributes = {})
557
+ @to_a = attributes.fetch(:items, []).freeze
558
+ @page_number = attributes.fetch(:page_number, 0)
559
+ @page_size = attributes.fetch(:page_size, 0)
560
+ @total_pages = attributes.fetch(:total_pages, 0)
561
+ @total_elements = attributes.fetch(:total_elements, 0)
562
+ @first = attributes.fetch(:first, true)
563
+ @last = attributes.fetch(:last, true)
564
+ @raw = attributes.fetch(:raw, {})
565
+ end
566
+
567
+ # @yieldparam item [Object]
568
+ # @return [Enumerator, self]
569
+ def each(&block)
570
+ return to_enum(:each) unless block
571
+
572
+ to_a.each(&block)
573
+
574
+ self
575
+ end
576
+
577
+ # @return [Integer] how many items are on this page
578
+ def size
579
+ to_a.size
580
+ end
581
+ alias length size
582
+
583
+ # @return [Boolean] whether this page has no items
584
+ def empty?
585
+ to_a.empty?
586
+ end
587
+
588
+ # @param index [Integer]
589
+ # @return [Object, nil]
590
+ def [](index)
591
+ to_a[index]
592
+ end
593
+
594
+ # Whether this is the first page.
595
+ #
596
+ # @return [Boolean]
597
+ def first?
598
+ @first
599
+ end
600
+
601
+ # Whether this is the last page.
602
+ #
603
+ # @return [Boolean]
604
+ def last?
605
+ @last
606
+ end
607
+
608
+ # Whether another page follows this one.
609
+ #
610
+ # An empty page counts as the end even when the API did not mark it last, so a walk cannot
611
+ # spin on a list that shrank underneath it.
612
+ #
613
+ # @return [Boolean]
614
+ def next?
615
+ !last? && !empty?
616
+ end
617
+ alias has_next? next?
618
+
619
+ # @api private
620
+ # @param payload [Hash] a +V1Page+ from the API
621
+ # @param model [Class] the model each item is parsed into
622
+ def self.from_api(payload, model)
623
+ payload = Parsing.hash_or_empty(payload)
624
+ items = Parsing.array_or_empty(payload["content"]).grep(Hash)
625
+
626
+ new(
627
+ items: items.map { |item| model.from_api(item) },
628
+ page_number: Parsing.integer_or_zero(payload["pageNumber"]),
629
+ page_size: Parsing.integer_or_zero(payload["pageSize"]),
630
+ total_pages: Parsing.integer_or_zero(payload["totalPages"]),
631
+ total_elements: Parsing.integer_or_zero(payload["totalElements"]),
632
+ first: payload["first"] != false,
633
+ last: payload["last"] != false,
634
+ raw: payload
635
+ )
636
+ end
637
+ end
638
+
639
+ # A pointer to another resource: its id, a label for display, and its API path.
640
+ #
641
+ # @!attribute [r] id
642
+ # @return [String, nil] the resource's id
643
+ # @!attribute [r] label
644
+ # @return [String, nil] a name for display
645
+ # @!attribute [r] path
646
+ # @return [String, nil] the resource's path in the API
647
+ # @!attribute [r] raw
648
+ # @return [Hash] the reference as received
649
+ class Reference
650
+ attr_reader :id, :label, :path, :raw
651
+
652
+ # @api private
653
+ def initialize(id:, label: nil, path: nil, raw: {})
654
+ @id = id
655
+ @label = label
656
+ @path = path
657
+ @raw = raw
658
+ end
659
+
660
+ # @api private
661
+ # @return [Reference, nil] nil when the payload is not a hash
662
+ def self.from_api(payload)
663
+ return nil unless payload.is_a?(Hash)
664
+
665
+ new(id: payload["id"], label: payload["label"], path: payload["path"], raw: payload)
666
+ end
667
+ end
668
+
669
+ # A pointer to a group, as it appears on an event.
670
+ #
671
+ # @!attribute [r] type
672
+ # @return [String, nil] the kind of group, such as +"team"+
673
+ # @!attribute [r] id
674
+ # @return [String, nil] your own id for the group
675
+ # @!attribute [r] label
676
+ # @return [String, nil] a name for display
677
+ # @!attribute [r] path
678
+ # @return [String, nil] the group's path in the API
679
+ # @!attribute [r] raw
680
+ # @return [Hash] the reference as received
681
+ class GroupReference
682
+ attr_reader :type, :id, :label, :path, :raw
683
+
684
+ # @api private
685
+ def initialize(type:, id:, label: nil, path: nil, raw: {})
686
+ @type = type
687
+ @id = id
688
+ @label = label
689
+ @path = path
690
+ @raw = raw
691
+ end
692
+
693
+ # @api private
694
+ def self.from_api(payload)
695
+ payload = Parsing.hash_or_empty(payload)
696
+
697
+ new(
698
+ type: payload["type"],
699
+ id: payload["id"],
700
+ label: payload["label"],
701
+ path: payload["path"],
702
+ raw: payload
703
+ )
704
+ end
705
+ end
706
+
707
+ # An organization, team, workspace, or whatever your application groups users into.
708
+ #
709
+ # A group is identified by its +type+ and +id+ together, both your own. Dregs normalizes the type
710
+ # to lower_snake_case.
711
+ #
712
+ # @!attribute [r] type
713
+ # @return [String, nil] the kind of group, such as +"organization"+ or +"team"+
714
+ # @!attribute [r] id
715
+ # @return [String, nil] your own id for the group
716
+ # @!attribute [r] display_name
717
+ # @return [String, nil] a name for display, taken from the group's attributes
718
+ # @!attribute [r] identity_count
719
+ # @return [Integer, nil] how many identities are members
720
+ # @!attribute [r] created_at
721
+ # @return [Time, nil]
722
+ # @!attribute [r] updated_at
723
+ # @return [Time, nil]
724
+ # @!attribute [r] last_tracked_at
725
+ # @return [Time, nil] the most recent event sent with the group
726
+ # @!attribute [r] data
727
+ # @return [Hash] the group's attributes; empty on the groups of a list call, which leaves them
728
+ # out
729
+ # @!attribute [r] raw
730
+ # @return [Hash] the group as received
731
+ class Group
732
+ attr_reader :type, :id, :display_name, :identity_count, :created_at, :updated_at, :last_tracked_at,
733
+ :data, :raw
734
+
735
+ # @api private
736
+ def initialize(attributes)
737
+ @type = attributes[:type]
738
+ @id = attributes[:id]
739
+ @display_name = attributes[:display_name]
740
+ @identity_count = attributes[:identity_count]
741
+ @created_at = attributes[:created_at]
742
+ @updated_at = attributes[:updated_at]
743
+ @last_tracked_at = attributes[:last_tracked_at]
744
+ @data = attributes.fetch(:data, {})
745
+ @raw = attributes.fetch(:raw, {})
746
+ end
747
+
748
+ # @api private
749
+ def self.from_api(payload)
750
+ payload = Parsing.hash_or_empty(payload)
751
+
752
+ new(
753
+ type: payload["type"],
754
+ id: payload["id"],
755
+ display_name: payload["displayName"],
756
+ identity_count: payload["identityCount"],
757
+ created_at: Parsing.time(payload["createdAt"]),
758
+ updated_at: Parsing.time(payload["updatedAt"]),
759
+ last_tracked_at: Parsing.time(payload["lastTrackedAt"]),
760
+ data: Parsing.hash_or_empty(payload["data"]),
761
+ raw: payload
762
+ )
763
+ end
764
+ end
765
+
766
+ # An event Dregs recorded.
767
+ #
768
+ # @!attribute [r] id
769
+ # @return [String, nil] the event's id, either the one you sent or one Dregs generated
770
+ # @!attribute [r] timestamp
771
+ # @return [Time, nil] when the event happened
772
+ # @!attribute [r] identity
773
+ # @return [Reference, nil] the identity the event belongs to, if any
774
+ # @!attribute [r] groups
775
+ # @return [Array<GroupReference>] the groups the event was sent with
776
+ # @!attribute [r] fingerprint
777
+ # @return [String, nil] the device fingerprint, for events that carried a device signature
778
+ # @!attribute [r] session
779
+ # @return [String, nil] the browser session, for events from the tracker
780
+ # @!attribute [r] ip
781
+ # @return [String, nil] the address the event came from
782
+ # @!attribute [r] url
783
+ # @return [String, nil] the page URL, for events from the tracker
784
+ # @!attribute [r] source
785
+ # @return [String, nil] where the event came from, such as +"ruby-sdk"+
786
+ # @!attribute [r] name
787
+ # @return [String, nil] your name for the event, such as +"user.signup"+
788
+ # @!attribute [r] canonical_type
789
+ # @return [String, nil] the Dregs event type your name is mapped to, if it is mapped
790
+ # @!attribute [r] data
791
+ # @return [Hash] the event's attributes, with sensitive values redacted
792
+ # @!attribute [r] raw
793
+ # @return [Hash] the event as received
794
+ class Event
795
+ attr_reader :id, :timestamp, :identity, :groups, :fingerprint, :session, :ip, :url, :source, :name,
796
+ :canonical_type, :data, :raw
797
+
798
+ # @api private
799
+ def initialize(attributes)
800
+ @id = attributes[:id]
801
+ @timestamp = attributes[:timestamp]
802
+ @identity = attributes[:identity]
803
+ @groups = attributes.fetch(:groups, [])
804
+ @fingerprint = attributes[:fingerprint]
805
+ @session = attributes[:session]
806
+ @ip = attributes[:ip]
807
+ @url = attributes[:url]
808
+ @source = attributes[:source]
809
+ @name = attributes[:name]
810
+ @canonical_type = attributes[:canonical_type]
811
+ @data = attributes.fetch(:data, {})
812
+ @raw = attributes.fetch(:raw, {})
813
+ end
814
+
815
+ # @api private
816
+ def self.from_api(payload)
817
+ payload = Parsing.hash_or_empty(payload)
818
+ groups = Parsing.array_or_empty(payload["groups"]).grep(Hash)
819
+
820
+ new(
821
+ id: payload["id"],
822
+ timestamp: Parsing.time(payload["timestamp"]),
823
+ identity: Reference.from_api(payload["identity"]),
824
+ groups: groups.map { |group| GroupReference.from_api(group) },
825
+ fingerprint: payload["fingerprint"],
826
+ session: payload["session"],
827
+ ip: payload["ip"],
828
+ url: payload["url"],
829
+ source: payload["eventSource"],
830
+ name: payload["eventName"],
831
+ canonical_type: payload["canonicalType"],
832
+ data: Parsing.hash_or_empty(payload["eventData"]),
833
+ raw: payload
834
+ )
835
+ end
836
+ end
837
+
838
+ # A browser or device Dregs has seen, identified by its fingerprint.
839
+ #
840
+ # @!attribute [r] fingerprint
841
+ # @return [String, nil]
842
+ # @!attribute [r] ip
843
+ # @return [String, nil] the address it was last seen from
844
+ # @!attribute [r] asn
845
+ # @return [String, nil] the network the address belongs to
846
+ # @!attribute [r] asn_label
847
+ # @return [String, nil] a name for that network
848
+ # @!attribute [r] user_agent
849
+ # @return [String, nil]
850
+ # @!attribute [r] city
851
+ # @return [String, nil]
852
+ # @!attribute [r] region
853
+ # @return [String, nil]
854
+ # @!attribute [r] country
855
+ # @return [String, nil]
856
+ # @!attribute [r] country_code
857
+ # @return [String, nil]
858
+ # @!attribute [r] timezone
859
+ # @return [String, nil]
860
+ # @!attribute [r] latitude
861
+ # @return [Float, nil]
862
+ # @!attribute [r] longitude
863
+ # @return [Float, nil]
864
+ # @!attribute [r] disregarded
865
+ # @return [Boolean] whether the device no longer links the identities seen on it
866
+ # @!attribute [r] first_seen_at
867
+ # @return [Time, nil]
868
+ # @!attribute [r] last_seen_at
869
+ # @return [Time, nil]
870
+ # @!attribute [r] raw
871
+ # @return [Hash] the device as received
872
+ class Device
873
+ attr_reader :fingerprint, :ip, :asn, :asn_label, :user_agent, :city, :region, :country, :country_code,
874
+ :timezone, :latitude, :longitude, :disregarded, :first_seen_at, :last_seen_at, :raw
875
+
876
+ # @api private
877
+ def initialize(attributes)
878
+ @fingerprint = attributes[:fingerprint]
879
+ @ip = attributes[:ip]
880
+ @asn = attributes[:asn]
881
+ @asn_label = attributes[:asn_label]
882
+ @user_agent = attributes[:user_agent]
883
+ @city = attributes[:city]
884
+ @region = attributes[:region]
885
+ @country = attributes[:country]
886
+ @country_code = attributes[:country_code]
887
+ @timezone = attributes[:timezone]
888
+ @latitude = attributes[:latitude]
889
+ @longitude = attributes[:longitude]
890
+ @disregarded = attributes.fetch(:disregarded, false)
891
+ @first_seen_at = attributes[:first_seen_at]
892
+ @last_seen_at = attributes[:last_seen_at]
893
+ @raw = attributes.fetch(:raw, {})
894
+ end
895
+
896
+ # Whether the device no longer links the identities seen on it.
897
+ #
898
+ # @return [Boolean]
899
+ def disregarded?
900
+ disregarded
901
+ end
902
+
903
+ # @api private
904
+ def self.from_api(payload)
905
+ payload = Parsing.hash_or_empty(payload)
906
+
907
+ new(
908
+ fingerprint: payload["fingerprint"],
909
+ ip: payload["ip"],
910
+ asn: payload["asn"],
911
+ asn_label: payload["asnLabel"],
912
+ user_agent: payload["userAgent"],
913
+ city: payload["city"],
914
+ region: payload["region"],
915
+ country: payload["country"],
916
+ country_code: payload["countryCode"],
917
+ timezone: payload["timezone"],
918
+ latitude: payload["latitude"],
919
+ longitude: payload["longitude"],
920
+ disregarded: payload["disregarded"] == true,
921
+ first_seen_at: Parsing.time(payload["firstSeenAt"]),
922
+ last_seen_at: Parsing.time(payload["lastSeenAt"]),
923
+ raw: payload
924
+ )
925
+ end
926
+ end
927
+
928
+ # Another identity Dregs believes is connected to this one, such as by a shared device.
929
+ #
930
+ # @!attribute [r] identity_id
931
+ # @return [String, nil] the linked identity's id
932
+ # @!attribute [r] display_name
933
+ # @return [String, nil] the linked identity's display name
934
+ # @!attribute [r] type
935
+ # @return [String, nil] how they are linked, such as +"SAME_DEVICE"+ or +"SHARED_IP"+
936
+ # @!attribute [r] value
937
+ # @return [Float, nil] how strongly they are linked, from 0.0 to 1.0
938
+ # @!attribute [r] confidence
939
+ # @return [Float, nil] how sure Dregs is of the link, from 0.0 to 1.0
940
+ # @!attribute [r] explanation
941
+ # @return [String, nil] a sentence describing the link
942
+ # @!attribute [r] metadata
943
+ # @return [Hash] the details behind it
944
+ # @!attribute [r] created_at
945
+ # @return [Time, nil]
946
+ # @!attribute [r] raw
947
+ # @return [Hash] the link as received
948
+ class LinkedIdentity
949
+ attr_reader :identity_id, :display_name, :type, :value, :confidence, :explanation, :metadata,
950
+ :created_at, :raw
951
+
952
+ # @api private
953
+ def initialize(attributes)
954
+ @identity_id = attributes[:identity_id]
955
+ @display_name = attributes[:display_name]
956
+ @type = attributes[:type]
957
+ @value = attributes[:value]
958
+ @confidence = attributes[:confidence]
959
+ @explanation = attributes[:explanation]
960
+ @metadata = attributes.fetch(:metadata, {})
961
+ @created_at = attributes[:created_at]
962
+ @raw = attributes.fetch(:raw, {})
963
+ end
964
+
965
+ # @api private
966
+ def self.from_api(payload)
967
+ payload = Parsing.hash_or_empty(payload)
968
+
969
+ new(
970
+ identity_id: payload["targetIdentityId"],
971
+ display_name: payload["targetDisplayName"],
972
+ type: payload["type"],
973
+ value: payload["value"],
974
+ confidence: payload["confidence"],
975
+ explanation: payload["explanation"],
976
+ metadata: Parsing.hash_or_empty(payload["metadata"]),
977
+ created_at: Parsing.time(payload["createdAt"]),
978
+ raw: payload
979
+ )
980
+ end
981
+ end
982
+
983
+ # The scores one past analysis cycle produced.
984
+ #
985
+ # @!attribute [r] cycle_id
986
+ # @return [Integer, nil] the cycle's id, which {Identities#analysis} accepts as +cycle_id:+ to
987
+ # fetch that cycle's observations
988
+ # @!attribute [r] humanity
989
+ # @return [Integer, nil]
990
+ # @!attribute [r] authenticity
991
+ # @return [Integer, nil]
992
+ # @!attribute [r] uniqueness
993
+ # @return [Integer, nil]
994
+ # @!attribute [r] behavior
995
+ # @return [Integer, nil]
996
+ # @!attribute [r] event_count
997
+ # @return [Integer, nil] how many events the cycle considered
998
+ # @!attribute [r] device_count
999
+ # @return [Integer, nil] how many devices the cycle considered
1000
+ # @!attribute [r] finished_at
1001
+ # @return [Time, nil]
1002
+ # @!attribute [r] raw
1003
+ # @return [Hash] the entry as received
1004
+ class ScoreHistoryEntry
1005
+ attr_reader :cycle_id, :humanity, :authenticity, :uniqueness, :behavior, :event_count, :device_count,
1006
+ :finished_at, :raw
1007
+
1008
+ # @api private
1009
+ def initialize(attributes)
1010
+ @cycle_id = attributes[:cycle_id]
1011
+ @humanity = attributes[:humanity]
1012
+ @authenticity = attributes[:authenticity]
1013
+ @uniqueness = attributes[:uniqueness]
1014
+ @behavior = attributes[:behavior]
1015
+ @event_count = attributes[:event_count]
1016
+ @device_count = attributes[:device_count]
1017
+ @finished_at = attributes[:finished_at]
1018
+ @raw = attributes.fetch(:raw, {})
1019
+ end
1020
+
1021
+ # @api private
1022
+ def self.from_api(payload)
1023
+ payload = Parsing.hash_or_empty(payload)
1024
+
1025
+ new(
1026
+ cycle_id: payload["id"],
1027
+ humanity: payload["humanityScore"],
1028
+ authenticity: payload["authenticityScore"],
1029
+ uniqueness: payload["uniquenessScore"],
1030
+ behavior: payload["behaviorScore"],
1031
+ event_count: payload["eventCount"],
1032
+ device_count: payload["deviceCount"],
1033
+ finished_at: Parsing.time(payload["finishedAt"]),
1034
+ raw: payload
1035
+ )
1036
+ end
1037
+ end
1038
+
1039
+ # A review of an identity's analysis, written by Dregs's AI reviewer.
1040
+ #
1041
+ # @!attribute [r] id
1042
+ # @return [Integer, nil] the review's id
1043
+ # @!attribute [r] identity_id
1044
+ # @return [String, nil]
1045
+ # @!attribute [r] status
1046
+ # @return [String, nil] +"PENDING"+ until the review has run, then +"FINISHED"+ or +"FAILED"+
1047
+ # @!attribute [r] feedback
1048
+ # @return [String, nil] the note you sent with the request, if any
1049
+ # @!attribute [r] summary
1050
+ # @return [String, nil] the reviewer's findings, once it has run
1051
+ # @!attribute [r] error_message
1052
+ # @return [String, nil] why the review failed, when it did
1053
+ # @!attribute [r] created_at
1054
+ # @return [Time, nil]
1055
+ # @!attribute [r] completed_at
1056
+ # @return [Time, nil]
1057
+ # @!attribute [r] raw
1058
+ # @return [Hash] the review as received
1059
+ class IdentityReview
1060
+ attr_reader :id, :identity_id, :status, :feedback, :summary, :error_message, :created_at,
1061
+ :completed_at, :raw
1062
+
1063
+ # @api private
1064
+ def initialize(attributes)
1065
+ @id = attributes[:id]
1066
+ @identity_id = attributes[:identity_id]
1067
+ @status = attributes[:status]
1068
+ @feedback = attributes[:feedback]
1069
+ @summary = attributes[:summary]
1070
+ @error_message = attributes[:error_message]
1071
+ @created_at = attributes[:created_at]
1072
+ @completed_at = attributes[:completed_at]
1073
+ @raw = attributes.fetch(:raw, {})
1074
+ end
1075
+
1076
+ # @api private
1077
+ def self.from_api(payload)
1078
+ payload = Parsing.hash_or_empty(payload)
1079
+
1080
+ new(
1081
+ id: payload["id"],
1082
+ identity_id: payload["identityId"],
1083
+ status: payload["status"],
1084
+ feedback: payload["userFeedback"],
1085
+ summary: payload["summary"],
1086
+ error_message: payload["errorMessage"],
1087
+ created_at: Parsing.time(payload["createdAt"]),
1088
+ completed_at: Parsing.time(payload["completedAt"]),
1089
+ raw: payload
1090
+ )
1091
+ end
1092
+ end
1093
+
1094
+ # An escalation an escalation rule opened against an identity.
1095
+ #
1096
+ # @!attribute [r] id
1097
+ # @return [Integer, nil] the escalation's id
1098
+ # @!attribute [r] status
1099
+ # @return [String, nil] +"OPEN"+, +"ACKNOWLEDGED"+, or +"CLOSED"+
1100
+ # @!attribute [r] severity
1101
+ # @return [String, nil] +"INFO"+, +"WARNING"+, or +"CRITICAL"+
1102
+ # @!attribute [r] identity
1103
+ # @return [Reference, nil] the identity the escalation is about
1104
+ # @!attribute [r] rule
1105
+ # @return [Reference, nil] the escalation rule that opened it
1106
+ # @!attribute [r] scores
1107
+ # @return [Hash] the identity's scores when the escalation opened, by category name
1108
+ # @!attribute [r] opened_at
1109
+ # @return [Time, nil]
1110
+ # @!attribute [r] acknowledged_at
1111
+ # @return [Time, nil]
1112
+ # @!attribute [r] closed_at
1113
+ # @return [Time, nil]
1114
+ # @!attribute [r] created_at
1115
+ # @return [Time, nil]
1116
+ # @!attribute [r] raw
1117
+ # @return [Hash] the escalation as received
1118
+ class Escalation
1119
+ attr_reader :id, :status, :severity, :identity, :rule, :scores, :opened_at, :acknowledged_at, :closed_at,
1120
+ :created_at, :raw
1121
+
1122
+ # @api private
1123
+ def initialize(attributes)
1124
+ @id = attributes[:id]
1125
+ @status = attributes[:status]
1126
+ @severity = attributes[:severity]
1127
+ @identity = attributes[:identity]
1128
+ @rule = attributes[:rule]
1129
+ @scores = attributes.fetch(:scores, {})
1130
+ @opened_at = attributes[:opened_at]
1131
+ @acknowledged_at = attributes[:acknowledged_at]
1132
+ @closed_at = attributes[:closed_at]
1133
+ @created_at = attributes[:created_at]
1134
+ @raw = attributes.fetch(:raw, {})
1135
+ end
1136
+
1137
+ # @api private
1138
+ def self.from_api(payload)
1139
+ payload = Parsing.hash_or_empty(payload)
1140
+
1141
+ new(
1142
+ id: payload["id"],
1143
+ status: payload["status"],
1144
+ severity: payload["severity"],
1145
+ identity: Reference.from_api(payload["identity"]),
1146
+ rule: Reference.from_api(payload["escalationRule"]),
1147
+ scores: Parsing.hash_or_empty(payload["scoreSnapshot"]),
1148
+ opened_at: Parsing.time(payload["openedAt"]),
1149
+ acknowledged_at: Parsing.time(payload["acknowledgedAt"]),
1150
+ closed_at: Parsing.time(payload["closedAt"]),
1151
+ created_at: Parsing.time(payload["createdAt"]),
1152
+ raw: payload
1153
+ )
1154
+ end
1155
+ end
1156
+
1157
+ # How many escalations are in each status.
1158
+ #
1159
+ # @!attribute [r] open
1160
+ # @return [Integer]
1161
+ # @!attribute [r] acknowledged
1162
+ # @return [Integer]
1163
+ # @!attribute [r] closed
1164
+ # @return [Integer]
1165
+ # @!attribute [r] raw
1166
+ # @return [Hash] the summary as received
1167
+ class EscalationSummary
1168
+ attr_reader :open, :acknowledged, :closed, :raw
1169
+
1170
+ # @api private
1171
+ def initialize(open: 0, acknowledged: 0, closed: 0, raw: {})
1172
+ @open = open
1173
+ @acknowledged = acknowledged
1174
+ @closed = closed
1175
+ @raw = raw
1176
+ end
1177
+
1178
+ # @api private
1179
+ def self.from_api(payload)
1180
+ payload = Parsing.hash_or_empty(payload)
1181
+
1182
+ new(
1183
+ open: Parsing.integer_or_zero(payload["open"]),
1184
+ acknowledged: Parsing.integer_or_zero(payload["acknowledged"]),
1185
+ closed: Parsing.integer_or_zero(payload["closed"]),
1186
+ raw: payload
1187
+ )
1188
+ end
1189
+ end
520
1190
  end