cts-mpx 1.0.1

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +50 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +88 -0
  5. data/CONTRIBUTING +8 -0
  6. data/COPYRIGHT +10 -0
  7. data/EXAMPLES.md +81 -0
  8. data/Gemfile +4 -0
  9. data/Gemfile.lock +172 -0
  10. data/Guardfile +41 -0
  11. data/LICENSE +201 -0
  12. data/NOTICE +9 -0
  13. data/README.md +60 -0
  14. data/Rakefile +6 -0
  15. data/bin/console +14 -0
  16. data/bin/setup +8 -0
  17. data/config/data_services.json +423 -0
  18. data/config/ingest_services.json +14 -0
  19. data/config/root_registry_sea1.json +118 -0
  20. data/config/web_services.json +544 -0
  21. data/cts-mpx.gemspec +43 -0
  22. data/examples/basic_query.rb +23 -0
  23. data/examples/login.rb +7 -0
  24. data/examples/update_media.rb +16 -0
  25. data/examples/update_procedurally.rb +20 -0
  26. data/lib/cts/mpx.rb +42 -0
  27. data/lib/cts/mpx/driver.rb +47 -0
  28. data/lib/cts/mpx/driver/assemblers.rb +96 -0
  29. data/lib/cts/mpx/driver/connections.rb +41 -0
  30. data/lib/cts/mpx/driver/exceptions.rb +50 -0
  31. data/lib/cts/mpx/driver/helpers.rb +67 -0
  32. data/lib/cts/mpx/driver/page.rb +38 -0
  33. data/lib/cts/mpx/driver/request.rb +60 -0
  34. data/lib/cts/mpx/driver/response.rb +72 -0
  35. data/lib/cts/mpx/entries.rb +80 -0
  36. data/lib/cts/mpx/entry.rb +100 -0
  37. data/lib/cts/mpx/field.rb +38 -0
  38. data/lib/cts/mpx/fields.rb +120 -0
  39. data/lib/cts/mpx/query.rb +115 -0
  40. data/lib/cts/mpx/registry.rb +60 -0
  41. data/lib/cts/mpx/service.rb +70 -0
  42. data/lib/cts/mpx/services.rb +113 -0
  43. data/lib/cts/mpx/services/data.rb +124 -0
  44. data/lib/cts/mpx/services/ingest.rb +60 -0
  45. data/lib/cts/mpx/services/web.rb +90 -0
  46. data/lib/cts/mpx/user.rb +74 -0
  47. data/lib/cts/mpx/validators.rb +51 -0
  48. data/lib/cts/mpx/version.rb +6 -0
  49. data/sdk-ring-diagram.png +0 -0
  50. data/sdk-uml.png +0 -0
  51. data/uml.nomnoml +242 -0
  52. metadata +401 -0
@@ -0,0 +1,38 @@
1
+ module Cts
2
+ module Mpx
3
+ module Driver
4
+ # <Description>
5
+ # @attribute entries
6
+ # @return [Array] array of deserialized entries
7
+ # @attribute xmlns
8
+ # @return [Hash] active namespace received for this page
9
+ class Page
10
+ extend Creatable
11
+ include Enumerable
12
+
13
+ attribute name: 'entries', kind_of: Array
14
+ attribute name: 'xmlns', kind_of: Hash
15
+
16
+ def initialize
17
+ @entries = []
18
+ @xmlns = {}
19
+ end
20
+
21
+ # return a json copy of the object, useful for later interpreation
22
+ # @param [Object] indent_depth sets indent depth to 2 if not nil
23
+ # @return [String] json serialized copy of the page
24
+ def to_s(indent_depth = nil)
25
+ indent_depth = 2 if indent_depth
26
+
27
+ Oj.dump(
28
+ {
29
+ "xmlns" => xmlns,
30
+ "entries" => entries
31
+ },
32
+ indent: indent_depth
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,60 @@
1
+ module Cts
2
+ module Mpx
3
+ module Driver
4
+ #
5
+ # A single request of any type to the services.
6
+ #
7
+ # @attribute method
8
+ # @return [Symbol] type of rest method, GET, PUT, POST, DELETE
9
+ # @attribute url
10
+ # @return [String] url to make the request against
11
+ # @attribute query
12
+ # @return [Hash] query to send with the request
13
+ # @attribute payload
14
+ # @return [String] payload to be sent to the services
15
+ # @attribute response
16
+ # @return [Cts::Mpx::Driver::Response] response from the service
17
+ # @attribute headers
18
+ # @return [Hash] headers to transmit to the services along with the request
19
+ class Request
20
+ extend Creatable
21
+
22
+ attribute name: 'method', kind_of: Symbol
23
+ attribute name: 'url', kind_of: String
24
+ attribute name: 'query', kind_of: Hash
25
+ attribute name: 'payload', kind_of: String
26
+ attribute name: 'response', kind_of: ::Cts::Mpx::Driver::Response
27
+ attribute name: 'headers', kind_of: Hash
28
+
29
+ # Call the built request.
30
+ # @raise [RuntimeException] if the method is not a get, put, post or delete
31
+ # @raise [RuntimeException] if the url is not a valid reference
32
+ # @return [Cts::Mpx::Driver::Response] response from the service
33
+ def call
34
+ @headers ||= {}
35
+ @query ||= {}
36
+
37
+ call_exceptions method, url
38
+ socket = Connections[url]
39
+ params = {
40
+ headers: @headers,
41
+ path: URI.parse(url).path,
42
+ query: @query
43
+ }
44
+ params[:body] = payload if payload
45
+
46
+ r = socket.send method, params
47
+ @response = Response.create original: r
48
+ @response
49
+ end
50
+
51
+ private
52
+
53
+ def call_exceptions(method, url)
54
+ raise "#{method} is not a valid method" unless %i[get put post delete].include? method.downcase
55
+ raise "#{url} is not a valid reference" unless Cts::Mpx::Validators.reference? url
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,72 @@
1
+ module Cts
2
+ module Mpx
3
+ module Driver
4
+ #
5
+ # Class to contain a response from the services, has a few helper methods to make reading the data easier.
6
+ #
7
+ class Response
8
+ extend Creatable
9
+
10
+ # @!attribute original
11
+ # @return [Excon::Response] copy of the original excon response.
12
+ attribute name: 'original', kind_of: Excon::Response
13
+
14
+ #
15
+ # Hash output of the data returned from the services.
16
+ #
17
+ # @return [Hash] Hash including keys specific to the service and type of service.
18
+ #
19
+ def data
20
+ return @data if @data
21
+
22
+ raise 'response does not appear to be healthy' unless healthy?
23
+
24
+ begin
25
+ @data = Oj.load(original.body)
26
+ rescue Oj::ParseError => e
27
+ raise "could not parse data: #{e}"
28
+ end
29
+ @data
30
+ end
31
+
32
+ #
33
+ # Is the response healthy? did it have a status code outside 2xx or 3xx.
34
+ #
35
+ # @return [TrueFalse] false if status <= 199 or => 400, otherwise true.
36
+ #
37
+ def healthy?
38
+ return false if status <= 199 || status >= 400
39
+ true
40
+ end
41
+
42
+ #
43
+ # Does this response contain a service exception
44
+ #
45
+ # @return [TrueFalse] true if it does, false if it does not.
46
+ #
47
+ def service_exception?
48
+ data['isException'] == true
49
+ end
50
+
51
+ #
52
+ # a page of data, processes the response.data for any entries.
53
+ #
54
+ # @return [Cts::Mpx::Driver::Page] a page of data.
55
+ #
56
+ def page
57
+ raise 'response does not appear to be healthy' unless healthy?
58
+ Cts::Mpx::Driver::Page.create entries: data['entries'], xmlns: data['$xmlns']
59
+ end
60
+
61
+ #
62
+ # Status code of the response
63
+ #
64
+ # @return [Fixnum] http status code
65
+ #
66
+ def status
67
+ original.status || nil
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,80 @@
1
+ module Cts
2
+ module Mpx
3
+ # Enumerable collection that can store an entry in it
4
+ # @attribute collection storage for the individual entry
5
+ # @return [Entry[]]
6
+ class Entries
7
+ include Enumerable
8
+ include Driver
9
+ extend Creatable
10
+
11
+ attribute name: 'collection', kind_of: Array
12
+
13
+ # Create a new entries collection from a page
14
+ # @param [Page] page the page object to process
15
+ # @raise [ArgumentError] if :page is not available
16
+ # @return [Entries] a new entries collection
17
+ def self.create_from_page(page)
18
+ Exceptions.raise_unless_argument_error? page, Page
19
+ entries = new
20
+ page.entries.each do |e|
21
+ entry = Entry.new
22
+ entry.fields = Fields.create_from_data(data: e, xmlns: page.xmlns)
23
+ entry.id = entry.fields['id'] if entry.fields['id']
24
+ entries.add entry
25
+ end
26
+ entries
27
+ end
28
+
29
+ # Addressable method, indexed by entry object
30
+ # @param [Entry] key the entry to return
31
+ # @return [Self.collection,Entry,nil] Can return the collection, a single entry, or nil if nothing found
32
+ def [](key = nil)
33
+ return @collection unless key
34
+ @collection.find { |e| e.id == key }
35
+ end
36
+
37
+ # Add an entry object to the collection
38
+ # @param [Entry] entry instantiated Entry to include
39
+ # @raise [ArgumentError] if entry is not an Entry
40
+ # @return [Self]
41
+ def add(entry)
42
+ return self if @collection.include? entry
43
+ Exceptions.raise_unless_argument_error? entry, Entry
44
+ @collection.push entry
45
+ self
46
+ end
47
+
48
+ # Iterator method for self
49
+ # @return [Entry] next object in the list
50
+ def each
51
+ @collection.each { |c| yield c }
52
+ end
53
+
54
+ # Reset the entry array to a blank state
55
+ # @return [Void]
56
+ def initialize
57
+ reset
58
+ end
59
+
60
+ # Remove a entry object from the collection
61
+ # @param [Entry] argument instantiated Entry to remove
62
+ # @return [Self]
63
+ def remove(argument)
64
+ @collection.delete_if { |f| f == argument }
65
+ end
66
+
67
+ # Reset the entry array to a blank state
68
+ # @return [Void]
69
+ def reset
70
+ @collection = []
71
+ end
72
+
73
+ # A hash of all available entries
74
+ # @return [Hash]
75
+ def to_h
76
+ map(&:to_h)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,100 @@
1
+ module Cts
2
+ module Mpx
3
+ # ORM style class to contain any entry from the data services.
4
+ class Entry
5
+ extend Creatable
6
+
7
+ attribute name: 'endpoint', kind_of: String
8
+ attribute name: 'fields', kind_of: Fields
9
+ attribute name: 'id', kind_of: String
10
+ attribute name: 'service', kind_of: String
11
+
12
+ # Load a Entry based on a long form ID
13
+ # @param [User] user user to make calls with
14
+ # @param [String] id long form id to look up
15
+ # @param [String] fields comma delimited list of fields to collect
16
+ # @return [Entry] the resulting entry
17
+ def self.load_by_id(user: nil, id: nil, fields: nil)
18
+ Driver::Helpers.required_arguments %i[user id], binding
19
+
20
+ Driver::Exceptions.raise_unless_argument_error? user, User
21
+ Driver::Exceptions.raise_unless_reference? id
22
+
23
+ e = new
24
+ e.id = id
25
+ e.load user: user, fields: nil
26
+ e
27
+ end
28
+
29
+ # Return the id of the entry.
30
+ # @return [Entry] the resulting entry
31
+ def id
32
+ fields['id']
33
+ end
34
+
35
+ # Set the id of the entry, will check if it's a valid reference.
36
+ # @param [String] account_id account_id to set the entry to
37
+ # @return [Entry] the resulting entry
38
+ def id=(account_id)
39
+ Driver::Exceptions.raise_unless_reference? account_id
40
+ result = Services.from_url account_id
41
+ fields['id'] = account_id
42
+
43
+ @service = result[:service]
44
+ @endpoint = result[:endpoint]
45
+ end
46
+
47
+ # Initialize an entry.
48
+ # Currently only instantiates fields.
49
+ def initialize
50
+ @fields = Fields.new
51
+ end
52
+
53
+ # Return a [Hash] of the entry.
54
+ # @return [Hash] includes keys xmlns: [Hash] and entries: [Fields]
55
+ def to_h
56
+ {
57
+ xmlns: fields.xmlns,
58
+ entry: fields.to_h
59
+ }
60
+ end
61
+
62
+ # Load data from the remote services based on the id.
63
+ # @param [User] user user to make calls with
64
+ # @param [String] fields comma delimited list of fields to collect
65
+ # @return [Driver::Response] Response of the call.
66
+ def load(user: nil, fields: nil)
67
+ Driver::Helpers.required_arguments %i[user], binding
68
+
69
+ Driver::Exceptions.raise_unless_argument_error? user, User
70
+ Driver::Exceptions.raise_unless_argument_error? fields, String if fields
71
+ Driver::Exceptions.raise_unless_reference? id
72
+
73
+ response = Services::Data.get user: user, service: service, endpoint: endpoint, fields: fields, ids: id.split("/").last
74
+ self.fields.parse data: response.data['entries'].first, xmlns: response.data['xmlns']
75
+ response
76
+ end
77
+
78
+ # Save the entry to the remote services.
79
+ # @param [User] user user to make calls with
80
+ # @return [Driver::Response] Response of the call.
81
+ def save(user: nil)
82
+ Driver::Helpers.required_arguments %i[user], binding
83
+ Driver::Exceptions.raise_unless_argument_error? user, User
84
+
85
+ p = Driver::Page.create entries: [fields.to_h], xmlns: fields.xmlns
86
+
87
+ if id
88
+ response = Services::Data.put user: user, service: service, endpoint: endpoint, page: p
89
+ else
90
+ raise ArgumentError, "service is a required keyword" unless service
91
+ raise ArgumentError, "endpoint is a required keyword" unless endpoint
92
+
93
+ response = Services::Data.post user: user, service: service, endpoint: endpoint, page: p
94
+ end
95
+
96
+ response
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,38 @@
1
+ module Cts
2
+ module Mpx
3
+ # Indivudal field, contains the name, value, and an optional namespace
4
+ # @attribute name name of the field
5
+ # @return [String]
6
+ # @attribute value value of the field
7
+ # @return [Object]
8
+ # @attribute xmlns namespace of the field
9
+ # @return [Hash]
10
+ class Field
11
+ extend Creatable
12
+
13
+ attribute name: 'name', kind_of: String
14
+ attribute name: 'value'
15
+ attribute name: 'xmlns', kind_of: Hash
16
+
17
+ # Return just the name value as key/value
18
+ # @return [Hash]
19
+ def to_h
20
+ { name => value }
21
+ end
22
+
23
+ # Determines if this field is a custom field or not
24
+ # @return [Symbol] :internal or :custom if it is a custom field
25
+ def type
26
+ return :custom if name.include? "$"
27
+ :internal
28
+ end
29
+
30
+ # Set the namespace of the field
31
+ # @param [Hash] xmlns namespace of the fields
32
+ # @return [Void]
33
+ def xmlns=(xmlns)
34
+ @xmlns = xmlns if name.include? '$'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,120 @@
1
+ module Cts
2
+ module Mpx
3
+ # Enumerable collection that can store an entry field in it
4
+ # @attribute collection storage for the individual field
5
+ # @return [Field[]]
6
+ class Fields
7
+ include Enumerable
8
+ extend Creatable
9
+
10
+ attribute name: 'collection', kind_of: Array
11
+
12
+ # Create a new fields collection from a data hash
13
+ # @param [Hash] data raw fields to add
14
+ # @param [Hash] xmlns namespace of the fields
15
+ # @raise [ArgumentError] if :data or :xmlns are not provided
16
+ # @return [Fields] a new fields collection
17
+ def self.create_from_data(data: nil, xmlns: nil)
18
+ Driver::Helpers.required_arguments([:data], binding)
19
+ obj = new
20
+ obj.parse(data: data, xmlns: xmlns)
21
+ obj
22
+ end
23
+
24
+ # Addressable method, indexed by field name
25
+ # @param [String] key name of the field
26
+ # @return [Self.collection,Field,nil] Can return the collection, a single field, or nil if nothing found
27
+ def [](key = nil)
28
+ return @collection unless key
29
+ result = @collection.find { |f| f.name == key }
30
+ return result.value if result
31
+ nil
32
+ end
33
+
34
+ # Addresable set method, indexed by field name
35
+ # @note will create a new copy if it is not found in the collection
36
+ # @param [String] key name of the field
37
+ # @param [Object] value value of the field
38
+ # @param [Hash] xmlns namespace of the field
39
+ # @example to include xmlns, you need to use the long format of this method
40
+ # fields.[]= 'id', 'value', xmlns: {}
41
+ # @return [Void]
42
+ def []=(key, value, xmlns: nil)
43
+ existing_field = find { |f| f.name == key }
44
+ if existing_field
45
+ existing_field.value = value
46
+ else
47
+ add Field.create name: key, value: value, xmlns: xmlns
48
+ end
49
+ end
50
+
51
+ # Add a field object to the collection
52
+ # @param [Field] field instantiated Field to include
53
+ # @raise [ArgumentError] if field is not a Field
54
+ # @return [Self]
55
+ def add(field)
56
+ return self if @collection.include? field
57
+ Driver::Exceptions.raise_unless_argument_error? field, Field
58
+ @collection.push field
59
+ self
60
+ end
61
+
62
+ # Iterator method for self
63
+ # @return [Field] next object in the list
64
+ def each
65
+ @collection.each { |c| yield c }
66
+ end
67
+
68
+ # Reset the field array to a blank state
69
+ # @return [Void]
70
+ def initialize
71
+ reset
72
+ end
73
+
74
+ # Parse two hashes into a field array
75
+ # @note this will also set the collection
76
+ # @param [Hash] xmlns namespace
77
+ # @param [Hash] data fields in hash form
78
+ # @raise [ArgumentError] if xmlns is not a Hash
79
+ # @return [Field[]] returns a collection of fields
80
+ def parse(xmlns: nil, data: nil)
81
+ Driver::Exceptions.raise_unless_argument_error? data, Hash
82
+ data.delete :service
83
+ data.delete :endpoint
84
+ reset
85
+ @collection = data.map { |k, v| Field.create name: k.to_s, value: v, xmlns: xmlns }
86
+ end
87
+
88
+ # Remove a field object from the collection
89
+ # @param [String] name instantiated Field to remove
90
+ # @return [Self]
91
+ def remove(name)
92
+ @collection.delete_if { |f| f.name == name }
93
+ end
94
+
95
+ # Reset the field array to a blank state
96
+ # @return [Void]
97
+ def reset
98
+ @collection = []
99
+ end
100
+
101
+ # return the fields as a hash
102
+ # @return [Hash] key is name, value is value
103
+ def to_h
104
+ h = {}
105
+ each { |f| h.store f.name, f.value }
106
+ h
107
+ end
108
+
109
+ # Return the cumulative namespace of all Field's in the collection
110
+ # @return [Hash] key is the namespace key, value is the value
111
+ def xmlns
112
+ a = collection.map(&:xmlns).uniq
113
+ a.delete nil
114
+ h = {}
115
+ a.each { |e| h.merge! e }
116
+ h
117
+ end
118
+ end
119
+ end
120
+ end