data_nexus 0.2.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.
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'member_consents'
4
+ require_relative 'member_enrollments'
5
+
6
+ module DataNexus
7
+ module Resources
8
+ # Resource for managing a specific member within a program context
9
+ #
10
+ # Provides methods for finding, updating, and accessing related resources
11
+ # for a specific member.
12
+ #
13
+ # @example Find a member
14
+ # member = client.programs("program-uuid").members("member-id").find
15
+ #
16
+ # @example Update a member
17
+ # client.programs("program-uuid").members("member-id").update(
18
+ # member: { phone_number: "+15551234567" }
19
+ # )
20
+ #
21
+ # @example Get household members
22
+ # household = client.programs("program-uuid").members("member-id").household
23
+ #
24
+ # @example Access consents
25
+ # client.programs("program-uuid").members("member-id").consents.create(
26
+ # consent: { category: "sms", member_response: true, consent_details: {} }
27
+ # )
28
+ #
29
+ # @example Access enrollments
30
+ # client.programs("program-uuid").members("member-id").enrollments.create(
31
+ # enrollment: { enrolled_at: "2024-01-01T00:00:00Z" }
32
+ # )
33
+ #
34
+ class ProgramMember
35
+ # @return [Connection] The HTTP connection
36
+ attr_reader :connection
37
+
38
+ # @return [String] The program UUID
39
+ attr_reader :program_id
40
+
41
+ # @return [String] The member ID
42
+ attr_reader :member_id
43
+
44
+ # Initialize a new ProgramMember resource
45
+ #
46
+ # @param connection [Connection] The HTTP connection
47
+ # @param program_id [String] The program UUID
48
+ # @param member_id [String] The member ID
49
+ def initialize(connection, program_id, member_id)
50
+ @connection = connection
51
+ @program_id = program_id
52
+ @member_id = member_id
53
+ end
54
+
55
+ # Find this member
56
+ #
57
+ # @return [Hash] The member data
58
+ #
59
+ # @example
60
+ # member = client.programs("uuid").members("member-id").find
61
+ # puts member[:first_name]
62
+ def find
63
+ response = connection.get("#{base_path}/#{member_id}")
64
+ response[:data]
65
+ end
66
+
67
+ # Update this member's attributes
68
+ #
69
+ # @param member [Hash] The member attributes to update
70
+ # @return [Hash] Response containing :data with the updated member
71
+ #
72
+ # @example
73
+ # response = client.programs("uuid").members("member-id").update(
74
+ # member: { phone_number: "+15551234567" }
75
+ # )
76
+ # updated_member = response[:data]
77
+ def update(member:)
78
+ body = { member: member }
79
+ connection.patch("#{base_path}/#{member_id}", body)
80
+ end
81
+
82
+ # Get household members for this member
83
+ #
84
+ # @return [Array<Hash>] Array of household member data
85
+ #
86
+ # @example
87
+ # household = client.programs("uuid").members("member-id").household
88
+ # household.each { |m| puts "#{m[:first_name]} - #{m[:relationship_type]}" }
89
+ def household
90
+ response = connection.get("#{base_path}/#{member_id}/household")
91
+ response[:data]
92
+ end
93
+
94
+ # Access consents for this member
95
+ #
96
+ # @return [MemberConsents] The member consents resource
97
+ #
98
+ # @example Create a consent
99
+ # client.programs("uuid").members("member-id").consents.create(
100
+ # consent: { category: "sms", member_response: true, consent_details: {} }
101
+ # )
102
+ #
103
+ # @example Find a consent
104
+ # client.programs("uuid").members("member-id").consents.find(123)
105
+ def consents
106
+ MemberConsents.new(connection, member_id, program_id)
107
+ end
108
+
109
+ # Access enrollments for this member
110
+ #
111
+ # @return [MemberEnrollments] The member enrollments resource
112
+ #
113
+ # @example Create an enrollment
114
+ # client.programs("uuid").members("member-id").enrollments.create(
115
+ # enrollment: { enrolled_at: "2024-01-01T00:00:00Z" }
116
+ # )
117
+ #
118
+ # @example Find an enrollment
119
+ # client.programs("uuid").members("member-id").enrollments.find(123)
120
+ def enrollments
121
+ MemberEnrollments.new(connection, member_id, program_id)
122
+ end
123
+
124
+ private
125
+
126
+ # Base path for program members endpoints
127
+ #
128
+ # @return [String]
129
+ def base_path
130
+ "/api/programs/#{program_id}/members"
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataNexus
4
+ module Resources
5
+ # Resource for listing program members
6
+ #
7
+ # Provides methods for listing and searching members within a specific program.
8
+ # For operations on a specific member, use `programs("uuid").members("member-id")`.
9
+ #
10
+ # @example List members with filters
11
+ # client.programs("uuid").members.list(
12
+ # first_name: "george",
13
+ # born_on: "1976-07-04"
14
+ # )
15
+ #
16
+ # @example Paginate through members
17
+ # collection = client.programs("uuid").members.list(first: 50)
18
+ # collection.each_page { |page| process(page.data) }
19
+ #
20
+ class ProgramMembers
21
+ # @return [Connection] The HTTP connection
22
+ attr_reader :connection
23
+
24
+ # @return [String] The program UUID
25
+ attr_reader :program_id
26
+
27
+ # Initialize a new ProgramMembers resource
28
+ #
29
+ # @param connection [Connection] The HTTP connection
30
+ # @param program_id [String] The program UUID
31
+ def initialize(connection, program_id)
32
+ @connection = connection
33
+ @program_id = program_id
34
+ end
35
+
36
+ # List program members with optional filters
37
+ #
38
+ # @param first_name [String, nil] Filter by exact first name
39
+ # @param first_name_prefix [String, nil] Filter by first name prefix (first initial)
40
+ # @param last_name [String, nil] Filter by exact last name
41
+ # @param last_name_prefix [String, nil] Filter by last name prefix (min 3 chars)
42
+ # @param born_on [String, nil] Filter by date of birth (YYYY-MM-DD)
43
+ # @param employee_id [String, nil] Filter by employee ID
44
+ #
45
+ # @return [Collection] Paginated collection of members
46
+ #
47
+ # @example Basic listing
48
+ # collection = client.programs("uuid").members.list
49
+ # collection.data.each { |m| puts m[:first_name] }
50
+ #
51
+ # @example With filters
52
+ # collection = client.programs("uuid").members.list(
53
+ # born_on: "1976-07-04",
54
+ # last_name_prefix: "was"
55
+ # )
56
+ def list(**params)
57
+ allowed_params = %i[
58
+ first_name first_name_prefix
59
+ last_name last_name_prefix
60
+ born_on employee_id
61
+ ]
62
+
63
+ query_params = params.slice(*allowed_params).compact
64
+ response = connection.get(base_path, query_params)
65
+ Collection.new(response, resource: self, params: query_params)
66
+ end
67
+
68
+ private
69
+
70
+ # Base path for program members endpoints
71
+ #
72
+ # @return [String]
73
+ def base_path
74
+ "/api/programs/#{program_id}/members"
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'program_member'
4
+ require_relative 'program_members'
5
+
6
+ module DataNexus
7
+ module Resources
8
+ # Proxy for program-scoped resources
9
+ #
10
+ # This class provides access to resources that are scoped to a specific program,
11
+ # such as members, consents, and enrollments.
12
+ #
13
+ # @example List program members
14
+ # client.programs("program-uuid").members.list
15
+ #
16
+ # @example Access a specific member
17
+ # client.programs("program-uuid").members("member-id").find
18
+ # client.programs("program-uuid").members("member-id").update(member: { phone_number: "..." })
19
+ # client.programs("program-uuid").members("member-id").consents.create(...)
20
+ # client.programs("program-uuid").members("member-id").enrollments.create(...)
21
+ #
22
+ # @example Search for members
23
+ # client.programs("program-uuid").search_members(born_on: "1976-07-04", employee_id: "ABC123")
24
+ #
25
+ class Programs
26
+ VALID_SEARCH_COMBINATIONS = [
27
+ %i[born_on first_name last_name employee_id],
28
+ %i[born_on first_name last_name],
29
+ %i[born_on first_name_prefix last_name_prefix employee_id],
30
+ %i[born_on first_name_prefix last_name_prefix],
31
+ %i[born_on employee_id]
32
+ ].freeze
33
+
34
+ # @return [Connection] The HTTP connection
35
+ attr_reader :connection
36
+
37
+ # @return [String] The program UUID
38
+ attr_reader :program_id
39
+
40
+ # Initialize a new Programs resource proxy
41
+ #
42
+ # @param connection [Connection] The HTTP connection
43
+ # @param program_id [String] The program UUID
44
+ def initialize(connection, program_id)
45
+ @connection = connection
46
+ @program_id = program_id
47
+ end
48
+
49
+ # Access program members
50
+ #
51
+ # When called without an argument, returns a resource for listing members.
52
+ # When called with a member_id, returns a resource for that specific member.
53
+ #
54
+ # @param member_id [String, nil] The member ID (optional)
55
+ # @return [ProgramMembers] When no member_id provided - for listing members
56
+ # @return [ProgramMember] When member_id provided - for member-specific operations
57
+ #
58
+ # @example List members
59
+ # client.programs("uuid").members.list
60
+ #
61
+ # @example Access a specific member
62
+ # client.programs("uuid").members("member-id").find
63
+ #
64
+ # @example Update a member
65
+ # client.programs("uuid").members("member-id").update(member: { first_name: "George" })
66
+ #
67
+ # @example Access member consents
68
+ # client.programs("uuid").members("member-id").consents.create(...)
69
+ #
70
+ # @example Access member enrollments
71
+ # client.programs("uuid").members("member-id").enrollments.create(...)
72
+ def members(member_id = nil)
73
+ if member_id
74
+ ProgramMember.new(connection, program_id, member_id)
75
+ else
76
+ ProgramMembers.new(connection, program_id)
77
+ end
78
+ end
79
+
80
+ # Search for members within this program
81
+ #
82
+ # Returns a bounded result set (max 10 results). Use `more_results` to
83
+ # determine if additional matches exist beyond what was returned.
84
+ #
85
+ # Valid parameter combinations:
86
+ # - born_on, first_name, last_name, employee_id
87
+ # - born_on, first_name, last_name
88
+ # - born_on, first_name_prefix, last_name_prefix, employee_id
89
+ # - born_on, first_name_prefix, last_name_prefix
90
+ # - born_on, employee_id
91
+ #
92
+ # @param born_on [String] Date of birth (YYYY-MM-DD) - required for all searches
93
+ # @param first_name [String, nil] Exact first name match
94
+ # @param first_name_prefix [String, nil] First name prefix (min 1 char)
95
+ # @param last_name [String, nil] Exact last name match
96
+ # @param last_name_prefix [String, nil] Last name prefix (min 3 chars)
97
+ # @param employee_id [String, nil] Employee ID
98
+ #
99
+ # @return [Hash] Response with :data (Array) and :more_results (Boolean)
100
+ #
101
+ # @raise [ArgumentError] If params don't match a valid search combination
102
+ #
103
+ # @example Search by name and DOB
104
+ # client.programs("uuid").search_members(
105
+ # born_on: "1976-07-04",
106
+ # first_name: "george",
107
+ # last_name: "washington"
108
+ # )
109
+ #
110
+ # @example Search by prefix and DOB
111
+ # client.programs("uuid").search_members(
112
+ # born_on: "1976-07-04",
113
+ # first_name_prefix: "g",
114
+ # last_name_prefix: "was"
115
+ # )
116
+ #
117
+ # @example Search by employee ID and DOB
118
+ # client.programs("uuid").search_members(
119
+ # born_on: "1976-07-04",
120
+ # employee_id: "ABC1234"
121
+ # )
122
+ def search_members(**params)
123
+ validate_search_params!(params)
124
+
125
+ connection.post("/api/programs/#{program_id}/members/search", params)
126
+ end
127
+
128
+ private
129
+
130
+ def validate_search_params!(params)
131
+ provided_keys = params.keys.sort
132
+
133
+ return if VALID_SEARCH_COMBINATIONS.any? { |combo| combo.sort == provided_keys }
134
+
135
+ raise ArgumentError, invalid_search_params_message(provided_keys)
136
+ end
137
+
138
+ def invalid_search_params_message(provided_keys)
139
+ valid_combos = VALID_SEARCH_COMBINATIONS.map { |c| c.join(', ') }.join("\n - ")
140
+
141
+ "Invalid search parameter combination: #{provided_keys.join(', ')}. " \
142
+ "Valid combinations are:\n - #{valid_combos}"
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DataNexus
4
+ VERSION = '0.2.0'
5
+ end
data/lib/data_nexus.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'data_nexus/version'
4
+ require_relative 'data_nexus/errors'
5
+ require_relative 'data_nexus/configuration'
6
+ require_relative 'data_nexus/connection'
7
+ require_relative 'data_nexus/client'
8
+ require_relative 'data_nexus/collection'
9
+ require_relative 'data_nexus/resources/programs'
10
+ require_relative 'data_nexus/resources/program_members'
11
+
12
+ # Ruby client library for the DataNexus API
13
+ #
14
+ # @example Configure and use the client
15
+ # client = DataNexus::Client.new(api_key: "your_api_key")
16
+ # members = client.programs("program-uuid").members.list
17
+ #
18
+ module DataNexus
19
+ class << self
20
+ # Global configuration instance
21
+ #
22
+ # @return [Configuration]
23
+ attr_writer :configuration
24
+
25
+ # Get the global configuration, initializing if needed
26
+ #
27
+ # @return [Configuration]
28
+ def configuration
29
+ @configuration ||= Configuration.new
30
+ end
31
+
32
+ # Configure the gem globally
33
+ #
34
+ # @example
35
+ # DataNexus.configure do |config|
36
+ # config.api_key = "your_api_key"
37
+ # config.base_url = "https://api.datanexus.com"
38
+ # end
39
+ #
40
+ # @yield [Configuration]
41
+ def configure
42
+ yield(configuration)
43
+ end
44
+
45
+ # Reset the global configuration
46
+ #
47
+ # @return [Configuration] A fresh configuration instance
48
+ def reset_configuration!
49
+ @configuration = Configuration.new
50
+ end
51
+ end
52
+ end
data/sig/datanexus.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Datanexus
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: data_nexus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Kibler
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-02-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday-retry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ description: A Ruby gem for interacting with the Dart Health's DataNexus API.
42
+ email:
43
+ - alexkibler@me.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".github/workflows/ci.yml"
49
+ - ".mise.local.toml.example"
50
+ - ".mise.toml"
51
+ - ".rubocop.yml"
52
+ - README.md
53
+ - Rakefile
54
+ - lib/data_nexus.rb
55
+ - lib/data_nexus/client.rb
56
+ - lib/data_nexus/collection.rb
57
+ - lib/data_nexus/configuration.rb
58
+ - lib/data_nexus/connection.rb
59
+ - lib/data_nexus/errors.rb
60
+ - lib/data_nexus/resources/member_consents.rb
61
+ - lib/data_nexus/resources/member_enrollments.rb
62
+ - lib/data_nexus/resources/members.rb
63
+ - lib/data_nexus/resources/program_member.rb
64
+ - lib/data_nexus/resources/program_members.rb
65
+ - lib/data_nexus/resources/programs.rb
66
+ - lib/data_nexus/version.rb
67
+ - sig/datanexus.rbs
68
+ homepage: https://github.com/DartHealth/datanexus-ruby
69
+ licenses:
70
+ - MIT
71
+ metadata:
72
+ homepage_uri: https://github.com/DartHealth/datanexus-ruby
73
+ source_code_uri: https://github.com/DartHealth/datanexus-ruby
74
+ changelog_uri: https://github.com/DartHealth/datanexus-ruby/releases
75
+ rubygems_mfa_required: 'true'
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 3.0.0
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.5.9
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Ruby client for the DataNexus API
95
+ test_files: []