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,115 @@
1
+ module Cts
2
+ module Mpx
3
+ # Query method, allows you to build and send a query to the data services.
4
+ # @attribute account
5
+ # @return [String] account account context, can be id or name
6
+ # @attribute endpoint
7
+ # @return [String] endpoint of the service
8
+ # @attribute extra_path
9
+ # @return [String] any extra_path
10
+ # @attribute fields
11
+ # @return [String] Fields to gather
12
+ # @attribute ids
13
+ # @return [Array] Ids to search through
14
+ # @attribute page
15
+ # @return [Page] Page of the search results
16
+ # @attribute query
17
+ # @return [Hash] Additional query to add
18
+ # @attribute range
19
+ # @return [String] String range in the service shell style
20
+ # @attribute return_count
21
+ # @return [Boolean] should this query return count (generally, no)
22
+ # @attribute return_entries
23
+ # @return [Boolean] should this query return entries
24
+ # @attribute service
25
+ # @return [String] service the query is for
26
+ # @attribute sort
27
+ # @return [String] string to sort in the service shell style
28
+ class Query
29
+ extend Creatable
30
+ attr_accessor :sort
31
+
32
+ attribute name: 'account', kind_of: String
33
+ attribute name: 'endpoint', kind_of: String
34
+ attribute name: 'extra_path', kind_of: String
35
+ attribute name: 'fields', kind_of: String
36
+ attribute name: 'ids', kind_of: Array
37
+ attribute name: 'page', kind_of: Driver::Page
38
+ attribute name: 'query', kind_of: Hash
39
+ attribute name: 'range', kind_of: String
40
+ attribute name: 'return_count', kind_of: FalseClass
41
+ attribute name: 'return_entries', kind_of: TrueClass
42
+ attribute name: 'service', kind_of: String
43
+ attribute name: 'sort', kind_of: String
44
+
45
+ # List of attributes availble
46
+ # @return [Symbol[]]
47
+ def attributes
48
+ %i[account endpoint extra_path fields ids query range return_count return_entries service sort]
49
+ end
50
+
51
+ # Instiatiate a page and query, set return's to false.
52
+ def initialize
53
+ @page = Driver::Page.new
54
+
55
+ @return_entries = true
56
+ @return_count = false
57
+ @query = {}
58
+ end
59
+
60
+ # List of entries created from the page
61
+ # @return [Entries] populated Entries object
62
+ def entries
63
+ Entries.create_from_page @page
64
+ end
65
+
66
+ # Run the query
67
+ # @param [User] user user to make calls with
68
+ # @return [Self]
69
+ def run(user: nil)
70
+ Driver::Helpers.required_arguments %i[user], binding
71
+ Driver::Exceptions.raise_unless_argument_error? user, User
72
+
73
+ raise "service must be set" unless service
74
+ raise "endpoint must be set" unless endpoint
75
+
76
+ response = Services::Data.get params.merge(user: user)
77
+ @page = response.page
78
+ self
79
+ end
80
+
81
+ # Hash representation of the query data. Has a key for params and for entries.
82
+ # @param [Boolean] include_entries include the entries array or not
83
+ # @return [Hash]
84
+ def to_h(include_entries: true)
85
+ h = {
86
+ params: params
87
+ }
88
+ h[:entries] = entries.to_h if include_entries
89
+ h
90
+ end
91
+
92
+ private
93
+
94
+ # List of parameters that are currently set in the query
95
+ # @return [Hash]
96
+ def params
97
+ output = {}
98
+
99
+ attributes.each do |attribute|
100
+ output.store attribute, instance_variable_get("@#{attribute}") unless instance_variable_get("@#{attribute}").nil?
101
+ end
102
+
103
+ unless output[:return_count].nil?
104
+ output[:count] = output.delete :return_count
105
+ end
106
+
107
+ unless output[:return_entries].nil?
108
+ output[:entries] = output.delete :return_entries
109
+ end
110
+
111
+ output
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,60 @@
1
+ module Cts
2
+ module Mpx
3
+ # Set of procedural functions to interact with the Registry.
4
+ module Registry
5
+ module_function
6
+
7
+ # Collection of domains stored in memory
8
+ # @return [Hash] key is an account_id, value is a hash of results
9
+ def domains
10
+ @domains
11
+ end
12
+
13
+ # Call fetch and store domain in sequence.
14
+ # @param [User] user user to make calls with
15
+ # @param [String] account_id long form account id (ownerId)
16
+ # @raise (see #fetch_domain)
17
+ # @return [Hash] hash of the newly fetched domain
18
+ def fetch_and_store_domain(user, account_id = 'urn:theplatform:auth:root')
19
+ result = fetch_domain user, account_id
20
+ store_domain result, account_id
21
+ domains[account_id]
22
+ end
23
+
24
+ # Fetch a domain from the registry.
25
+ # @param [User] user user to make calls with
26
+ # @param [String] account_id long form account id (ownerId)
27
+ # @raise [ArgumentError] if the user is not a user object
28
+ # @raise [ArgumentError] if the account_id is not valid
29
+ # @return [Hash] hash of the newly fetched domain
30
+ def fetch_domain(user, account_id = 'urn:theplatform:auth:root')
31
+ return domains['urn:theplatform:auth:root'] if account_id == 'urn:theplatform:auth:root'
32
+ Driver::Exceptions.raise_unless_argument_error?(user, 'User') { user.is_a? User }
33
+ Driver::Exceptions.raise_unless_argument_error?(account_id, 'account_id') { Validators.account_id? account_id }
34
+ user.token!
35
+
36
+ response = Services::Web.post user: user, service: 'Access Data Service', endpoint: 'Registry', method: 'resolveDomain', arguments: { 'accountId' => account_id }
37
+ response.data['resolveDomainResponse']
38
+ end
39
+
40
+ # Store the domain in memory
41
+ # @param [Hash] data collection received from Registry
42
+ # @param [String] account_id long form account id (ownerId)
43
+ # @raise [ArgumentError] if the data is not a valid hash
44
+ # @raise [ArgumentError] if the account_id is not valid
45
+ # @return [Void]
46
+ def store_domain(data, account_id = 'urn:theplatform:auth:root')
47
+ raise ArgumentError, "#{account_id} is not a valid account_id" unless Validators.account_id? account_id
48
+ raise ArgumentError, "#{data} is not a valid Hash" unless data.is_a? Hash
49
+ @domains.store account_id, data
50
+ nil
51
+ end
52
+
53
+ # find and store the root registry from the US
54
+ def initialize
55
+ @domains = {}
56
+ store_domain(Driver.load_json_file("#{Driver.config_dir}/root_registry_sea1.json")['resolveDomainResponse'], 'urn:theplatform:auth:root')
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,70 @@
1
+ module Cts
2
+ module Mpx
3
+ module Driver
4
+ # Class to wrap a service and what is available to interact with
5
+ # @attribute name
6
+ # @return [String] Title of the service, must be identical to the registry entry
7
+ # @attribute uri_hint
8
+ # @return [String] partial string of the uri, this allows for a reverse lookup from url
9
+ # @attribute path
10
+ # @return [String] prepended path statement attached per service
11
+ # @attribute form
12
+ # @return [String] type of http schema to use, can be json or cjson
13
+ # @attribute schema
14
+ # @return [String] version of the service schema
15
+ # @attribute search_schema
16
+ # @return [String] version of the search schema
17
+ # @attribute read_only
18
+ # @return [Boolean] is this a read only service or not
19
+ # @attribute endpoints
20
+ # @return [Array] Available endpoints to communicate with
21
+ # @attribute type
22
+ # @return [String] the type of service this is
23
+ class Service
24
+ attr_accessor :name
25
+ attr_accessor :uri_hint
26
+ attr_accessor :path
27
+ attr_accessor :form
28
+ attr_accessor :schema
29
+ attr_accessor :search_schema
30
+ attr_accessor :read_only
31
+ attr_accessor :endpoints
32
+ attr_accessor :type
33
+
34
+ # Set all values to something
35
+ def initialize
36
+ @name = ""
37
+ @uri_hint = ""
38
+ @path = ""
39
+ @form = ""
40
+ @schema = ""
41
+ @search_schema = ""
42
+ @read_only = false
43
+ @endpoints = []
44
+ @type = nil
45
+ @url = nil
46
+ end
47
+
48
+ # Return a given url of a service, will look in the local registry
49
+ # @param [String] account_id long form account id (ownerId)
50
+ # @return [String] the url string or nil if none available
51
+ def url(account_id = 'urn:theplatform:auth:root')
52
+ Exceptions.raise_unless_account_id account_id
53
+ reg = Registry.domains[account_id]
54
+ return reg[name] if reg
55
+ nil
56
+ end
57
+
58
+ # checks if we have a given entry in the local registry
59
+ # @param [String] account_id long form account id (ownerId)
60
+ # @return [Boolean] true if it is available, false if it is not
61
+ def url?(account_id = 'urn:theplatform:auth:root')
62
+ Exceptions.raise_unless_account_id account_id
63
+ u = url account_id
64
+ return true if u
65
+ false
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,113 @@
1
+ module Cts
2
+ module Mpx
3
+ # Container style module for the collection of services available.
4
+ module Services
5
+ module_function
6
+
7
+ # Addressable method, indexed by service title
8
+ # @param [String] key service title to look up the service object
9
+ # @raise [ArgumentError] if the key is not a service name
10
+ # @raise [ArgumentError] if the key is not a string
11
+ # @return [Service[]] if no key, return the entire array of services
12
+ # @return [Service] a service
13
+ def [](key = nil)
14
+ return @services unless key
15
+ raise 'key must be a string' unless key.is_a? String
16
+ service = @services.find { |e| e.name == key }
17
+ raise "#{key} must be a service name." unless service
18
+ service
19
+ end
20
+
21
+ # return a service from the supplied url
22
+ # @param [String] url url to parse
23
+ # @return [Hash] including service and endpoint as string.
24
+ def from_url(url)
25
+ service = Services[].find { |s| url.include? s.uri_hint if s.uri_hint }
26
+ return nil unless service
27
+
28
+ {
29
+ service: service.name,
30
+ endpoint: /data\/([A-Z].*)\//.match(url)[1]
31
+ }
32
+ end
33
+
34
+ # Load references and services from disk.
35
+ def initialize
36
+ load_references
37
+ load_services
38
+ end
39
+
40
+ # Load the specified reference file into the container
41
+ # @param [<Type>] file file to load
42
+ # @param [<Type>] type type of service the file contains
43
+ # @return [Void]
44
+ def load_reference_file(file: nil, type: nil)
45
+ raise ArgumentError, 'type must be supplied' unless type
46
+ raise ArgumentError, 'file must be supplied' unless file
47
+ @raw_reference.store(type, Driver.load_json_file(file))
48
+ true
49
+ end
50
+
51
+ # Load all available reference files into memory
52
+ # @return [Void]
53
+ def load_references
54
+ @raw_reference = {}
55
+ Services.types.each do |type|
56
+ gemdir = if Gem::Specification.find_all.map(&:name).include? 'cts-mpx'
57
+ Gem::Specification.find_by_name('cts-mpx').gem_dir
58
+ else
59
+ # :nocov:
60
+ Dir.pwd
61
+ # :nocov:
62
+ end
63
+
64
+ Services.load_reference_file(file: "#{gemdir}/config/#{type}_services.json", type: type.to_s)
65
+ end
66
+ end
67
+
68
+ # Convert the raw reference into a service and add it to the stack of available serrvices
69
+ # @return [Void]
70
+ def load_services
71
+ @services = []
72
+ raw_reference.each do |type, services|
73
+ services.each do |name, data|
74
+ s = Driver::Service.new
75
+ s.name = name
76
+ s.uri_hint = data['uri_hint']
77
+ s.path = data['path']
78
+ s.form = data['form']
79
+ s.schema = data['schema']
80
+ s.search_schema = data['search_schema']
81
+ s.read_only = data['read_only'] ? true : false
82
+ s.endpoints = data['endpoints']
83
+ s.type = type
84
+ s.instance_variable_set :@url, data['url'] if data['url']
85
+ @services.push s
86
+ end
87
+ end
88
+ end
89
+
90
+ # Raw copy of the reference files
91
+ # @return [Hash] key is name, value is the data
92
+ def raw_reference
93
+ @raw_reference
94
+ end
95
+
96
+ # Single reference from the raw collection.
97
+ # @param [String] key service title
98
+ # @return [Hash] values of the reference
99
+ def reference(key = nil)
100
+ return @raw_reference unless key
101
+ raise 'key must be a string' unless key.is_a? String
102
+ raise "#{key} is not in the reference library." unless @raw_reference.include? key
103
+ @raw_reference[key]
104
+ end
105
+
106
+ # list of possible types of services
107
+ # @return [Symbol[]] List of all types
108
+ def types
109
+ %i[web data ingest]
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,124 @@
1
+ module Cts
2
+ module Mpx
3
+ module Services
4
+ # Collection of procedural methods to interact with the Data services
5
+ # All of these methods mimic the DSS clients as close as possible
6
+ # If your operation does not work in the DSS client, it will not work here
7
+ module Data
8
+ module_function
9
+
10
+ # Addressable method, indexed by data service title
11
+ # @param [String] key service title to look up the service object
12
+ # @raise [ArgumentError] if the key is not a service name
13
+ # @raise [ArgumentError] if the key is not a string
14
+ # @return [Service[]] if no key, return the entire array of services
15
+ # @return [Service] a service
16
+ def [](key = nil)
17
+ services = Services[].select { |s| s.type == 'data' }
18
+ return services unless key
19
+ raise ArgumentError, 'key must be a string' unless key.is_a? String
20
+ service = services.find { |e| e.name == key }
21
+ raise ArgumentError, "#{key} must be a service name." unless service
22
+ service
23
+ end
24
+
25
+ # Procedural method to DELETE data from the data services
26
+ # @raise (see #get)
27
+ # @param (see #get)
28
+ # @return (see #get)
29
+ def delete(user: nil, account: nil, service: nil, endpoint: nil, sort: nil, extra_path: nil, range: nil, ids: nil, query: {}, headers: {}, count: nil, entries: nil)
30
+ get(user: user, account: account, service: service, endpoint: endpoint, sort: sort, extra_path: extra_path, range: range, ids: ids, query: query, headers: headers, count: count, entries: entries, method: :delete)
31
+ end
32
+
33
+ # Procedural method to GET data from the data services
34
+ # @param [Boolean] count ask for a count of objects from the services
35
+ # @param [Boolean] entries return an array of entries
36
+ # @param [User] user user to make calls with
37
+ # @param [Hash] query additional parameters to add to the http call
38
+ # @param [Hash] headers additional headers to attach to the http call
39
+ # @param [String] account context account id or name
40
+ # @param [String] endpoint endpoint to make the call against
41
+ # @param [String] extra_path additional part to add to the path
42
+ # @param [String] fields comma delimited list of fields to collect
43
+ # @param [String] ids comma delimited list of short id's to add to the path
44
+ # @param [String] range string (service) format of a range
45
+ # @param [String] service title of a service
46
+ # @param [String] sort set the sort field
47
+ # @raise (see #prep_call)
48
+ # @return [Response] Response of the call
49
+ def get(user: nil, account: nil, service: nil, fields: nil, endpoint: nil, sort: nil, extra_path: nil, range: nil, ids: nil, query: {}, headers: {}, count: nil, entries: nil, method: :get)
50
+ prep_call(user: user, account: account, service: service, query: query, headers: headers, required_arguments: ['user', 'service', 'endpoint'], binding: binding)
51
+
52
+ host = Driver::Assemblers.host user: user, service: service
53
+ path = Driver::Assemblers.path service: service, endpoint: endpoint, extra_path: extra_path, ids: ids
54
+ query = Driver::Assemblers.query user: user, account: account, service: service, endpoint: endpoint, query: query
55
+
56
+ if Services[service].type == 'data'
57
+ query.merge! Driver::Assemblers.query_data range: range, count: count, entries: entries, sort: sort
58
+ query[:fields] = fields if fields
59
+ end
60
+ request = Driver::Request.create(method: method, url: [host, path].join, query: query, headers: headers)
61
+ request.call
62
+ end
63
+
64
+ # Procedural method to POST data to the data services
65
+ # @param [Driver::Page] page formated page to send to the data services
66
+ # @param [User] user user to make calls with
67
+ # @param [Hash] query additional parameters to add to the http call
68
+ # @param [Query] headers additional headers to attach to the http call
69
+ # @param [String] account account context, can be id or name
70
+ # @param [String] endpoint endpoint to make the call against
71
+ # @param [String] extra_path additional part to add to the path
72
+ # @param [String] service title of a service
73
+ # @raise (see #prep_call)
74
+ # @return [Response] Response of the call
75
+ def post(user: nil, account: nil, service: nil, endpoint: nil, extra_path: nil, query: {}, page: nil, headers: {}, method: :post)
76
+ prep_call(user: user, account: account, service: service, query: query, headers: headers, required_arguments: ['user', 'service', 'endpoint', 'page'], page: page, binding: binding)
77
+
78
+ host = Driver::Assemblers.host user: user, service: service
79
+ path = Driver::Assemblers.path service: service, endpoint: endpoint, extra_path: extra_path
80
+ query = Driver::Assemblers.query user: user, account: account, service: service, endpoint: endpoint, query: query
81
+
82
+ request = Driver::Request.create(method: method, url: [host, path].join, query: query, headers: headers, payload: page.to_s)
83
+ request.call
84
+ end
85
+
86
+ # Procedural method to PUT data to the data services
87
+ # @param (see #post)
88
+ # @raise (see #post)
89
+ # @return (see #post)
90
+ def put(user: nil, account: nil, service: nil, endpoint: nil, extra_path: nil, query: {}, page: nil, headers: {})
91
+ post(user: user, account: account, service: service, endpoint: endpoint, extra_path: extra_path, query: query, page: page, headers: headers, method: :put)
92
+ end
93
+
94
+ # Helper method to assure that everything is is ok to call the methods above
95
+ # @param [Hash] args params to assure are correct
96
+ # @option args [Symbol] :account account to apply to account_context lookups
97
+ # @option args [Symbol] :binding local binding (goes with required arguments)
98
+ # @option args [Symbol] :headers header object to check
99
+ # @option args [Symbol] :page page to send to the data service
100
+ # @option args [Symbol] :query query object to test
101
+ # @option args [Symbol] :required_arguments list of arguments required
102
+ # @option args [Symbol] :service title of the service
103
+ # @option args [Symbol] :user user to make calls with
104
+ # @raise [ArgumentError] if :query or :headers values are not a Hash
105
+ # @raise [ArgumentError] if the :page value is not a Page
106
+ # @raise [ArgumentError] if the list of :required_arguments are not set
107
+ # @raise [ArgumentError] if the :user does not have a token
108
+ # @raise (see Registry#fetch_domain)
109
+ # @raise (see Registry#store_domain)
110
+ # @return [Response] Response of the call
111
+ # @private
112
+ def prep_call(args = {})
113
+ Driver::Helpers.required_arguments args[:required_arguments], args[:binding]
114
+ args[:user].token!
115
+ Driver::Helpers.raise_if_not_a_hash [args[:query], args[:headers]]
116
+
117
+ Driver::Helpers.raise_if_not_a([args[:page]], Driver::Page) if args[:page]
118
+
119
+ Registry.fetch_and_store_domain(args[:user], args[:account]) unless self[args[:service]].url?
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end