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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -2
- data/README.md +119 -1
- data/lib/dregs/client.rb +111 -10
- data/lib/dregs/devices.rb +70 -0
- data/lib/dregs/errors.rb +43 -1
- data/lib/dregs/escalations.rb +107 -0
- data/lib/dregs/events.rb +73 -0
- data/lib/dregs/groups.rb +137 -0
- data/lib/dregs/identities.rb +135 -18
- data/lib/dregs/models.rb +670 -0
- data/lib/dregs/resource.rb +117 -0
- data/lib/dregs/version.rb +1 -1
- data/lib/dregs.rb +5 -0
- data/sig/dregs.rbs +418 -3
- metadata +7 -2
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
|