vantaca 0.3.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 17d644bafa17b33e40ce3695a575773dc6a4c32500b20463e5a85849d9e7f7ce
4
+ data.tar.gz: 1d1644fde830360cec3b38860f124528a32411613b5d0d4693866f01f2984bcf
5
+ SHA512:
6
+ metadata.gz: 69c22312ab4573a406982fed1c14ada85cc2bbeed1b4dbc197266ffda5e6b07d924588575739feeb6b1887a7c04b916b53d7017dde71cfce1712cd6b3959c9fe
7
+ data.tar.gz: 205fef825382c52847c32aa83d8ae304cd7bd74372e40602e22aa97fbcb3af181ae1a3eb7c5169d077bdc350d42ac9c0bdf173bd2ab428a3b322a6a474e92093
@@ -0,0 +1,30 @@
1
+ name: RSpec
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ os: [ ubuntu ]
15
+ ruby: [ 4.0, head ]
16
+
17
+ runs-on: ${{ matrix.os }}-latest
18
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
19
+
20
+ steps:
21
+ - uses: actions/checkout@v2
22
+ - name: Set up Ruby
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: ${{ matrix.ruby }}
26
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically:
27
+ - name: Install dependencies
28
+ run: bundle install
29
+ - name: Run tests
30
+ run: bundle exec rake
data/.rubocop.yml ADDED
@@ -0,0 +1,58 @@
1
+ plugins:
2
+ - rubocop-rake
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 4.0
6
+ # Always enable new cops, disabling manually when they don't fit.
7
+ EnabledByDefault: true
8
+ NewCops: enable
9
+
10
+ # ------------------------------------------------------------------------------
11
+ # Cops excluded for specific directories
12
+ # ------------------------------------------------------------------------------
13
+
14
+ Metrics/BlockLength:
15
+ Exclude:
16
+ - 'spec/**/*'
17
+
18
+ Style/Copyright:
19
+ AutocorrectNotice: |
20
+ # Copyright (c) Valencia Management Group
21
+ # All rights reserved.
22
+ Notice: |
23
+ Copyright \(c\) Valencia Management Group
24
+ All rights reserved.
25
+
26
+ # ------------------------------------------------------------------------------
27
+ # Cops that should not be run
28
+ # ------------------------------------------------------------------------------
29
+
30
+ Layout/MultilineMethodArgumentLineBreaks:
31
+ Enabled: false
32
+
33
+ # Recommends `::File` instead of `File`
34
+ Lint/ConstantResolution:
35
+ Enabled: false
36
+
37
+ # Recommends Integer(xxx, 10) instead of xxx.to_i
38
+ Lint/NumberConversion:
39
+ Enabled: false
40
+
41
+ Style/ConstantVisibility:
42
+ Enabled: false
43
+
44
+ # Every method should be documented, but I'm not *that* professional.
45
+ Style/DocumentationMethod:
46
+ Enabled: false
47
+
48
+ # Converts `.to eq 1` to `.to(eq(1))`
49
+ Style/MethodCallWithArgsParentheses:
50
+ Enabled: false
51
+
52
+ # This is just for setting HTTParty's request headers
53
+ Style/StringHashKeys:
54
+ Enabled: false
55
+
56
+ # Forbids the use of "rubocop:disable"
57
+ Style/DisableCopsWithinSourceCodeDirective:
58
+ Enabled: false
@@ -0,0 +1,14 @@
1
+ {
2
+ "[ruby]": {
3
+ "editor.rulers": [120],
4
+ "editor.tabSize": 2,
5
+ },
6
+ "[yaml]": {
7
+ "editor.rulers": [120],
8
+ "editor.tabSize": 2,
9
+ },
10
+ "files.insertFinalNewline": true,
11
+ "files.trimTrailingWhitespace": true,
12
+ "ruby.intellisense": false,
13
+ "ruby.useLanguageServer": true
14
+ }
data/Gemfile ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ source 'https://rubygems.org'
7
+
8
+ gemspec
9
+
10
+ # Gem management
11
+ gem 'bundler', '~> 4.0'
12
+
13
+ # Deployment tasks
14
+ gem 'rake', '~> 13.0'
15
+
16
+ # Test with RSpec
17
+ gem 'rspec', '~> 3.12'
18
+
19
+ # Ruby code linting
20
+ gem 'rubocop', '~> 1.54'
21
+
22
+ # Rubocop - rake cops
23
+ gem 'rubocop-rake', '~> 0.6'
24
+
25
+ # Rubocop - rspec cops
26
+ gem 'rubocop-rspec', '~> 3.0'
27
+
28
+ # Replay full HTTP interactions
29
+ gem 'vcr', '~> 6.2'
30
+
31
+ # Mock HTTP requests so that we're not hitting the actual Vantaca servers
32
+ gem 'webmock', '~> 3.18'
33
+
34
+ # Output logging
35
+ gem 'logger', '~> 1.7'
data/Gemfile.lock ADDED
@@ -0,0 +1,153 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ vantaca (0.3.0)
5
+ httparty (~> 0.24)
6
+ zeitwerk (~> 2.7)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.8.9)
12
+ public_suffix (>= 2.0.2, < 8.0)
13
+ ast (2.4.3)
14
+ bigdecimal (4.0.1)
15
+ crack (1.0.1)
16
+ bigdecimal
17
+ rexml
18
+ csv (3.3.5)
19
+ diff-lcs (1.6.2)
20
+ hashdiff (1.2.1)
21
+ httparty (0.24.2)
22
+ csv
23
+ mini_mime (>= 1.0.0)
24
+ multi_xml (>= 0.5.2)
25
+ json (2.19.1)
26
+ json-schema (6.2.0)
27
+ addressable (~> 2.8)
28
+ bigdecimal (>= 3.1, < 5)
29
+ language_server-protocol (3.17.0.5)
30
+ lint_roller (1.1.0)
31
+ logger (1.7.0)
32
+ mcp (0.8.0)
33
+ json-schema (>= 4.1)
34
+ mini_mime (1.1.5)
35
+ multi_xml (0.8.1)
36
+ bigdecimal (>= 3.1, < 5)
37
+ parallel (1.27.0)
38
+ parser (3.3.10.2)
39
+ ast (~> 2.4.1)
40
+ racc
41
+ prism (1.9.0)
42
+ public_suffix (7.0.5)
43
+ racc (1.8.1)
44
+ rainbow (3.1.1)
45
+ rake (13.3.1)
46
+ regexp_parser (2.11.3)
47
+ rexml (3.4.4)
48
+ rspec (3.13.2)
49
+ rspec-core (~> 3.13.0)
50
+ rspec-expectations (~> 3.13.0)
51
+ rspec-mocks (~> 3.13.0)
52
+ rspec-core (3.13.6)
53
+ rspec-support (~> 3.13.0)
54
+ rspec-expectations (3.13.5)
55
+ diff-lcs (>= 1.2.0, < 2.0)
56
+ rspec-support (~> 3.13.0)
57
+ rspec-mocks (3.13.8)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.13.0)
60
+ rspec-support (3.13.7)
61
+ rubocop (1.85.1)
62
+ json (~> 2.3)
63
+ language_server-protocol (~> 3.17.0.2)
64
+ lint_roller (~> 1.1.0)
65
+ mcp (~> 0.6)
66
+ parallel (~> 1.10)
67
+ parser (>= 3.3.0.2)
68
+ rainbow (>= 2.2.2, < 4.0)
69
+ regexp_parser (>= 2.9.3, < 3.0)
70
+ rubocop-ast (>= 1.49.0, < 2.0)
71
+ ruby-progressbar (~> 1.7)
72
+ unicode-display_width (>= 2.4.0, < 4.0)
73
+ rubocop-ast (1.49.1)
74
+ parser (>= 3.3.7.2)
75
+ prism (~> 1.7)
76
+ rubocop-rake (0.7.1)
77
+ lint_roller (~> 1.1)
78
+ rubocop (>= 1.72.1)
79
+ rubocop-rspec (3.9.0)
80
+ lint_roller (~> 1.1)
81
+ rubocop (~> 1.81)
82
+ ruby-progressbar (1.13.0)
83
+ unicode-display_width (3.2.0)
84
+ unicode-emoji (~> 4.1)
85
+ unicode-emoji (4.2.0)
86
+ vcr (6.4.0)
87
+ webmock (3.26.1)
88
+ addressable (>= 2.8.0)
89
+ crack (>= 0.3.2)
90
+ hashdiff (>= 0.4.0, < 2.0.0)
91
+ zeitwerk (2.7.5)
92
+
93
+ PLATFORMS
94
+ arm64-darwin-25
95
+ ruby
96
+
97
+ DEPENDENCIES
98
+ bundler (~> 4.0)
99
+ logger (~> 1.7)
100
+ rake (~> 13.0)
101
+ rspec (~> 3.12)
102
+ rubocop (~> 1.54)
103
+ rubocop-rake (~> 0.6)
104
+ rubocop-rspec (~> 3.0)
105
+ vantaca!
106
+ vcr (~> 6.2)
107
+ webmock (~> 3.18)
108
+
109
+ CHECKSUMS
110
+ addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485
111
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
112
+ bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
113
+ crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e
114
+ csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f
115
+ diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
116
+ hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
117
+ httparty (0.24.2) sha256=8fca6a54aa0c4aa4303a0fd33e5e2156175d6a5334f714263b458abd7fda9c38
118
+ json (2.19.1) sha256=dd94fdc59e48bff85913829a32350b3148156bc4fd2a95a2568a78b11344082d
119
+ json-schema (6.2.0) sha256=e8bff46ed845a22c1ab2bd0d7eccf831c01fe23bb3920caa4c74db4306813666
120
+ language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
121
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
122
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
123
+ mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb
124
+ mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
125
+ multi_xml (0.8.1) sha256=addba0290bac34e9088bfe73dc4878530297a82a7bbd66cb44dcd0a4b86edf5a
126
+ parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
127
+ parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357
128
+ prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
129
+ public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
130
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
131
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
132
+ rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
133
+ regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
134
+ rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
135
+ rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
136
+ rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
137
+ rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
138
+ rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
139
+ rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
140
+ rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77
141
+ rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
142
+ rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
143
+ rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
144
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
145
+ unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
146
+ unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
147
+ vantaca (0.3.0)
148
+ vcr (6.4.0) sha256=077ac92cc16efc5904eb90492a18153b5e6ca5398046d8a249a7c96a9ea24ae6
149
+ webmock (3.26.1) sha256=4f696fb57c90a827c20aadb2d4f9058bbff10f7f043bd0d4c3f58791143b1cd7
150
+ zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd
151
+
152
+ BUNDLED WITH
153
+ 4.0.6
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Vantaca API Gem
2
+
3
+ [![Build Status](https://github.com/ValenciaMgmt/Vantaca/workflows/RSpec/badge.svg?branch=master)](https://github.com/ValenciaMgmt/Vantaca/actions?query=workflow%3A%22RSpec%22)
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ require 'bundler/gem_tasks'
7
+ require 'rspec/core/rake_task'
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task default: :spec
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ # Methods which interact with Action Items
8
+ module ActionItems
9
+ # Load all active action categories.
10
+ #
11
+ # @return [Array<Vantaca::Models::ActionCategory>] active action categories
12
+ def action_categories
13
+ response = get('/read/actionCategoryList')
14
+
15
+ return [] unless response
16
+
17
+ Array(response).map { Vantaca::Models::ActionCategory.new(it) }
18
+ end
19
+
20
+ def action_types
21
+ response = get('/read/actionTypeList')
22
+
23
+ return [] unless response
24
+
25
+ Array(response).map { Vantaca::Models::ActionType.new(it) }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ # Methods which allow creation, modification, and deletion of homeowner addresses.
8
+ module Addresses
9
+ def create_address(homeowner_id, address_attributes)
10
+ post('/write/addressCreate', address_attributes.merge(hoid: homeowner_id))
11
+ end
12
+
13
+ def update_address(address_id, address_attributes)
14
+ post('/write/addressUpdate', address_attributes.merge(addrID: address_id))
15
+ end
16
+
17
+ def delete_address(address_id)
18
+ post('/write/addressDestroy', { addrID: address_id })
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ require 'logger'
7
+
8
+ module Vantaca
9
+ # The client which enables communication with the Vantaca API
10
+ class Client
11
+ include HTTParty
12
+
13
+ include Vantaca::ActionItems
14
+ include Vantaca::Addresses
15
+ include Vantaca::Communities
16
+ include Vantaca::Documents
17
+ include Vantaca::Emails
18
+ include Vantaca::Ledger
19
+ include Vantaca::Owners
20
+ include Vantaca::Phones
21
+ include Vantaca::Providers
22
+
23
+ headers 'Content-Type' => 'application/json'
24
+
25
+ base_uri 'https://service-e.vantaca.net/'
26
+
27
+ attr_accessor :logger
28
+
29
+ def initialize(logger: IO::NULL)
30
+ @logger = logger_for(logger)
31
+ end
32
+
33
+ def get(endpoint, **query)
34
+ response = self.class.get endpoint, query: query.merge(default_params)
35
+
36
+ raise_exception(response) unless (200..299).include? response.code
37
+
38
+ response.parsed_response
39
+ end
40
+
41
+ def put(endpoint, body, **query)
42
+ response = self.class.put endpoint, query: query.merge(default_params), body: body.to_json
43
+
44
+ raise_exception(response) unless response.code == 200
45
+
46
+ response.parsed_response
47
+ end
48
+
49
+ def post(endpoint, body, **query)
50
+ response = self.class.post endpoint, query: query.merge(default_params), body: body.to_json
51
+
52
+ raise_exception(response) unless response.code == 200
53
+
54
+ # This is always going to be `nil` for POST requests, but it doesn't hurt to be consistent.
55
+ response.parsed_response
56
+ end
57
+
58
+ def download(endpoint, **params)
59
+ raise ArgumentError, 'Vantaca::Client#download requires a block' unless block_given?
60
+
61
+ Tempfile.open('download') do |file|
62
+ file.binmode
63
+
64
+ file.write download_raw_data(endpoint, **params)
65
+
66
+ yield file
67
+ end
68
+ end
69
+
70
+ protected
71
+
72
+ def default_params = Vantaca.configuration.to_params
73
+
74
+ def raise_exception(response)
75
+ case response.code
76
+ when 404 then raise Vantaca::Errors::NotFoundError, response
77
+ when 400..499 then raise Vantaca::Errors::ClientError, response
78
+ when 500..599
79
+ # These errors can largely be ignored - it's not our fault
80
+ raise Vantaca::Errors::TimeoutError, response if response.response.message['Timeout expired']
81
+
82
+ raise Vantaca::Errors::InternalError, response
83
+ else
84
+ # As far as I'm aware, Vantaca does not return 100 - 199 or 205 - 399.
85
+ raise Vantaca::Errors::ApiError, response
86
+ end
87
+ end
88
+
89
+ def download_raw_data(endpoint, **query)
90
+ response = self.class.get(
91
+ endpoint,
92
+ headers: { 'Content-Type' => 'application/octet-stream' },
93
+ query: query.merge(default_params)
94
+ )
95
+
96
+ raise_exception(response) unless response.code == 200
97
+
98
+ response
99
+ end
100
+
101
+ def logger_for(logger)
102
+ return logger if logger.is_a?(::Logger)
103
+
104
+ return Vantaca.configuration.logger if logger == IO::NULL && Vantaca.configuration.logger
105
+
106
+ ::Logger.new(logger)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ # Methods which load general information, but not owners, of one or more communities.
8
+ module Communities
9
+ COMMUNITY_PARAMETERS = { charges: :includeCharges, late_fees: :includeLateFees }.freeze
10
+
11
+ # Load a list of all communities actively managed.
12
+ #
13
+ # @return [Array<Vantaca::Models::Community>] An array of all communities managed.
14
+ def communities(**options)
15
+ params = community_parameters(nil, options)
16
+
17
+ get('/read/Association', **params).map { Vantaca::Models::Community.new(it) }
18
+ end
19
+
20
+ # Load a single community by its abbreviation.
21
+ #
22
+ # @param assoc_code [String] The 2-4 character association code of the community
23
+ # @return [Vantaca::Models::Community] The community with the indicated association code.
24
+ def community(assoc_code, **options)
25
+ params = community_parameters(assoc_code, options)
26
+
27
+ response = get('/read/Association', **params)
28
+
29
+ # The API sends a 204 No Content response if there's no matching community.
30
+ raise Vantaca::Errors::NotFoundError unless response
31
+
32
+ Vantaca::Models::Community.new response.first
33
+ end
34
+
35
+ protected
36
+
37
+ def community_parameters(community, options)
38
+ params = { assocCode: community }
39
+
40
+ COMMUNITY_PARAMETERS.values_at(*Array(options[:include])).compact.each do |vantaca_parameter|
41
+ params[vantaca_parameter] = true
42
+ end
43
+
44
+ params.compact
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ # The basic configuration object for the Vantaca client. I wish these weren't passed in the URL...
8
+ class Configuration
9
+ attr_reader :login, :password, :company, :logger
10
+
11
+ def company=(input)
12
+ raise ArgumentError, 'Invalid Vantaca company name' unless input&.match?(/\A\w+\z/)
13
+
14
+ @company = input
15
+ end
16
+
17
+ def login=(input)
18
+ raise ArgumentError, 'Invalid Vantaca login' unless input&.match?(/\A\w+\z/)
19
+
20
+ @login = input
21
+ end
22
+
23
+ def password=(input)
24
+ raise ArgumentError, 'Invalid Vantaca password' unless input&.match?(/\A\w+\z/)
25
+
26
+ @password = input
27
+ end
28
+
29
+ def logger=(new_logger)
30
+ raise ArgumentError, 'Logger must be an instance of the Logger class' unless new_logger.is_a?(::Logger)
31
+
32
+ @logger = new_logger
33
+ end
34
+
35
+ def to_params
36
+ {
37
+ company: @company,
38
+ login: @login,
39
+ pwd: @password
40
+ }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ # Methods which fetch lists of documents or a document itself
8
+ module Documents
9
+ def documents(community:)
10
+ response = get('/read/Association', assocCode: community, includeDocuments: true)
11
+
12
+ return [] unless response
13
+
14
+ response.dig(0, 'documents').map { Vantaca::Models::Document.new(it) }
15
+ end
16
+
17
+ # Download an actual file
18
+ # TODO: Add the zip=true parameter and unzip the file using Rubyzip
19
+ def document(community:, image_id:, &)
20
+ raise ArgumentError, 'Vantaca::Client#document requires a block' unless block_given?
21
+
22
+ download('/read/getDocument', assocCode: community, imgID: image_id, &)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ # Methods which allow creation, modification, and deletion of homeowner email addresses.
8
+ module Emails
9
+ # @param homeowner_id [Integer] The ID of the homeowner
10
+ # @param email_attrs [Hash] The email address details
11
+ # @option email_attrs [String] :email (required) The email address
12
+ # @option email_attrs [Boolean] :isPrimary Whether this is the primary email address
13
+ # @option email_attrs [String] :label Optional label for the email address (e.g., 'Work', 'Personal')
14
+ # @return [nil] The API response from the email creation endpoint
15
+ def create_email_address(homeowner_id, email_attrs)
16
+ post('/write/emailCreate', email_attrs.merge(hoid: homeowner_id))
17
+ end
18
+
19
+ # @param email_address_id [Integer] The ID of the email address to update
20
+ # @param email_attrs [Hash] The updated email address details
21
+ # @option email_attrs [String] :email (required) The email address
22
+ # @option email_attrs [Boolean] :isPrimary Whether this is the primary email address
23
+ # @option email_attrs [String] :label Optional label for the email address (e.g., 'Work', 'Personal')
24
+ # @return [nil] The API response from the email update endpoint
25
+ def update_email_address(email_address_id, email_attrs)
26
+ post('/write/emailUpdate', email_attrs.merge(emailID: email_address_id))
27
+ end
28
+
29
+ # @param email_address_id [Integer] The ID of the email address to delete
30
+ # @return [nil] The API response from the email deletion endpoint
31
+ def delete_email_address(email_address_id)
32
+ post('/write/emailDestroy', { emailID: email_address_id })
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) Valencia Management Group
4
+ # All rights reserved.
5
+
6
+ module Vantaca
7
+ module Errors
8
+ # The base error class which more specific API error classes inherit from.
9
+ class ApiError < ::RuntimeError
10
+ DEFAULT_MESSAGE = 'An unknown error occurred.'
11
+
12
+ def initialize(response = nil)
13
+ @response = response
14
+
15
+ super()
16
+ end
17
+
18
+ def to_s
19
+ return self.class::DEFAULT_MESSAGE unless @response
20
+
21
+ format(
22
+ '%<code>s: %<message>s (%<uri>s)',
23
+ code: @response.code,
24
+ # Vantaca doesn't put the error message in the response body - it's in the status header!
25
+ message: @response.response.message,
26
+ uri: filtered_uri
27
+ )
28
+ end
29
+
30
+ def filtered_uri = @response.request.last_uri.to_s.gsub(/(company|login|pwd)=(?:[a-z0-9]+)/i, '\1=[FILTERED]')
31
+ end
32
+
33
+ # The client submitted invalid information.
34
+ class ClientError < ApiError
35
+ DEFAULT_MESSAGE = 'An unknown client error occurred.'
36
+ end
37
+
38
+ # A request was made for a key/query that doesn't exist.
39
+ class NotFoundError < ClientError
40
+ DEFAULT_MESSAGE = 'The requested resource could not be found.'
41
+ end
42
+
43
+ # Something happened but we don't know what and it's probably not our fault.
44
+ class InternalError < ApiError
45
+ DEFAULT_MESSAGE = 'An unknown internal error occurred.'
46
+ end
47
+
48
+ # The API took too long to respond, but everything might be fine later.
49
+ class TimeoutError < InternalError
50
+ DEFAULT_MESSAGE = 'The request timed out. Please try again in a moment.'
51
+ end
52
+ end
53
+ end