raad-adip 0.1.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: d45ba15fe2dcf22492b860c54c3b931ce1dca1fea322e5390136562b9ec0c8d0
4
+ data.tar.gz: a93fa94f7c589f29cc665a9774ba23009c4420bcbab7012d032ae54c4786ca0c
5
+ SHA512:
6
+ metadata.gz: 29e04bbdca6ac02e7e8534d316f26a5bbfd9d4b3788a941c15e6aadea2fee4fb742ee881c32e8d150f4943ad46b0a8f4246cb071a956383c89987bc9b6268852
7
+ data.tar.gz: 36c6d55050b2720c98d7a65548d935980a094b8c19543a070e46919b319fb023389338bf779df10a52b0e79a75d9fa95e4eec4b27f196a45da81857a93a12808
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2025-05-01
4
+
5
+ - Initial release of the FaaAdip gem
6
+ - Added support for the following ADIP API endpoints:
7
+ - Airport search and retrieval
8
+ - Runway information
9
+ - Weather data (METAR, TAF)
10
+ - Facility information
11
+ - Nearby airport search
12
+ - Added validation for API parameters
13
+ - Set up basic documentation
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # FaaAdip
2
+
3
+ A Ruby gem for interacting with the FAA's Airport Data & Information Portal (ADIP) API.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'faa_adip'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ ```bash
16
+ $ bundle install
17
+ ```
18
+
19
+ Or install it yourself as:
20
+
21
+ ```bash
22
+ $ gem install faa_adip
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ First, obtain your API credentials from the [FAA External API Portal](https://external-api.faa.gov/).
28
+
29
+ ### Basic Usage
30
+
31
+ ```ruby
32
+ require 'faa_adip'
33
+
34
+ # Initialize with client credentials
35
+ client = FaaAdip.new(
36
+ 'your_client_id',
37
+ 'your_client_secret'
38
+ )
39
+
40
+ # Or use environment variables
41
+ # export FAA_ADIP_CLIENT_ID=your_client_id
42
+ # export FAA_ADIP_CLIENT_SECRET=your_client_secret
43
+ client = FaaAdip.new
44
+
45
+ # Search for airports
46
+ results = client.search_airports(
47
+ state: 'VA',
48
+ facility_use: 'PUBLIC',
49
+ page_size: 20
50
+ )
51
+
52
+ # Get information for a specific airport
53
+ airport = client.get_airport('KIAD')
54
+
55
+ # Get runway information
56
+ runways = client.get_runways('KIAD')
57
+
58
+ # Get weather information
59
+ metar = client.get_metar('KIAD')
60
+ taf = client.get_taf('KIAD')
61
+
62
+ # Find nearby airports
63
+ nearby = client.get_nearby_airports('KIAD', 30, facility_use: 'PUBLIC')
64
+ ```
65
+
66
+ ### Available Search Parameters
67
+
68
+ The `search_airports` method accepts the following parameters:
69
+
70
+ - `:airport_id` - Airport identifier (e.g., 'KIAD')
71
+ - `:site_number` - FAA site number
72
+ - `:loc_id` - Location identifier
73
+ - `:state` - Two-letter state code
74
+ - `:city` - City name
75
+ - `:county` - County name
76
+ - `:facility_name` - Airport name or partial name
77
+ - `:facility_use` - 'PUBLIC', 'PRIVATE', or 'MILITARY'
78
+ - `:latitude` - Latitude coordinate
79
+ - `:longitude` - Longitude coordinate
80
+ - `:radius` - Search radius in nautical miles (when used with lat/lon)
81
+ - `:page_size` - Number of results per page (default: 20, max: 100)
82
+ - `:page_num` - Page number (default: 1)
83
+
84
+ ### Endpoints
85
+
86
+ The gem provides access to the following endpoints:
87
+
88
+ #### Airport Information
89
+ - `search_airports(options = {})` - Search for airports using various criteria
90
+ - `get_airport(airport_id)` - Get detailed information for a specific airport
91
+
92
+ #### Runway Data
93
+ - `get_runways(airport_id)` - Get runway information for a specific airport
94
+ - `get_runway(airport_id, runway_id)` - Get detailed information for a specific runway
95
+
96
+ #### Weather Data
97
+ - `get_metar(airport_id)` - Get METAR (current weather) data for an airport
98
+ - `get_taf(airport_id)` - Get TAF (forecast) data for an airport
99
+
100
+ #### Facility Data
101
+ - `get_facilities(airport_id)` - Get facility information for an airport
102
+ - `get_nearby_airports(airport_id, radius = 20, options = {})` - Find airports near a specific airport
103
+
104
+ ## Development
105
+
106
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
107
+
108
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
109
+
110
+ ## Contributing
111
+
112
+ Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/faa_adip. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/yourusername/faa_adip/blob/main/CODE_OF_CONDUCT.md).
113
+
114
+ ## License
115
+
116
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
117
+
118
+ ## Code of Conduct
119
+
120
+ Everyone interacting in the FaaAdip project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yourusername/faa_adip/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,241 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module FaaAdip
8
+ # Service to interact with FAA's Airport Data & Information Portal (ADIP) API
9
+ # https://external-api.faa.gov/adip
10
+ #
11
+ # This service provides access to FAA's ADIP API, which contains information about airports,
12
+ # including facilities, runways, and other airport data.
13
+ class Service
14
+ BASE_URL = "https://external-api.faa.gov/adip/v1"
15
+
16
+ attr_reader :client_id, :client_secret
17
+
18
+ # Initialize the service with FAA API credentials
19
+ # @param client_id [String] FAA API client ID
20
+ # @param client_secret [String] FAA API client secret
21
+ def initialize(client_id = nil, client_secret = nil)
22
+ @client_id = client_id || ENV['FAA_ADIP_CLIENT_ID']
23
+ @client_secret = client_secret || ENV['FAA_ADIP_CLIENT_SECRET']
24
+
25
+ raise ArgumentError, "FAA ADIP client_id is required" unless @client_id
26
+ raise ArgumentError, "FAA ADIP client_secret is required" unless @client_secret
27
+ end
28
+
29
+ # =========================================================================
30
+ # Airport Search Endpoints
31
+ # =========================================================================
32
+
33
+ # Search for airports based on various criteria
34
+ # @param options [Hash] Search criteria parameters
35
+ # @option options [String] :airport_id Airport identifier (e.g., 'KIAD')
36
+ # @option options [String] :site_number FAA site number
37
+ # @option options [String] :loc_id Location identifier
38
+ # @option options [String] :state Two-letter state code
39
+ # @option options [String] :city City name
40
+ # @option options [String] :county County name
41
+ # @option options [String] :facility_name Airport name or partial name
42
+ # @option options [String] :facility_use 'PUBLIC', 'PRIVATE', or 'MILITARY'
43
+ # @option options [Float] :latitude Latitude coordinate
44
+ # @option options [Float] :longitude Longitude coordinate
45
+ # @option options [Float] :radius Search radius in nautical miles (when used with lat/lon)
46
+ # @option options [Integer] :page_size Number of results per page (default: 20, max: 100)
47
+ # @option options [Integer] :page_num Page number (default: 1)
48
+ # @return [Hash] Airport search results
49
+ def search_airports(options = {})
50
+ params = {}
51
+
52
+ # Map option keys to API parameter names
53
+ param_mapping = {
54
+ airport_id: :airportId,
55
+ site_number: :siteNumber,
56
+ loc_id: :locId,
57
+ state: :state,
58
+ city: :city,
59
+ county: :county,
60
+ facility_name: :facilityName,
61
+ facility_use: :facilityUse,
62
+ latitude: :latitude,
63
+ longitude: :longitude,
64
+ radius: :radius,
65
+ page_size: :pageSize,
66
+ page_num: :pageNum
67
+ }
68
+
69
+ # Process each option
70
+ param_mapping.each do |option_key, param_key|
71
+ if options.key?(option_key)
72
+ # Special validation for parameters
73
+ value = case option_key
74
+ when :facility_use
75
+ validate_enum(options[option_key], %w[PUBLIC PRIVATE MILITARY], 'facility_use')
76
+ when :radius
77
+ validate_range(options[option_key], 0.1, 100, 'radius')
78
+ when :page_size
79
+ validate_range(options[option_key], 1, 100, 'page_size').to_i
80
+ when :page_num
81
+ validate_range(options[option_key], 1, nil, 'page_num').to_i
82
+ else
83
+ options[option_key]
84
+ end
85
+
86
+ params[param_key] = value
87
+ end
88
+ end
89
+
90
+ get("/airports", params)
91
+ end
92
+
93
+ # Get detailed information for a specific airport by its ID
94
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
95
+ # @return [Hash] Detailed airport information
96
+ def get_airport(airport_id)
97
+ get("/airports/#{URI.encode_www_form_component(airport_id)}")
98
+ end
99
+
100
+ # =========================================================================
101
+ # Runway Data Endpoints
102
+ # =========================================================================
103
+
104
+ # Get runway information for a specific airport
105
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
106
+ # @return [Hash] Runway information for the airport
107
+ def get_runways(airport_id)
108
+ get("/airports/#{URI.encode_www_form_component(airport_id)}/runways")
109
+ end
110
+
111
+ # Get detailed information for a specific runway
112
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
113
+ # @param runway_id [String] Runway identifier (e.g., '01/19')
114
+ # @return [Hash] Detailed runway information
115
+ def get_runway(airport_id, runway_id)
116
+ get("/airports/#{URI.encode_www_form_component(airport_id)}/runways/#{URI.encode_www_form_component(runway_id)}")
117
+ end
118
+
119
+ # =========================================================================
120
+ # Weather Data Endpoints
121
+ # =========================================================================
122
+
123
+ # Get METAR (Meteorological Aerodrome Report) data for a specific airport
124
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
125
+ # @return [Hash] METAR data for the airport
126
+ def get_metar(airport_id)
127
+ get("/airports/#{URI.encode_www_form_component(airport_id)}/weather/metar")
128
+ end
129
+
130
+ # Get TAF (Terminal Aerodrome Forecast) data for a specific airport
131
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
132
+ # @return [Hash] TAF data for the airport
133
+ def get_taf(airport_id)
134
+ get("/airports/#{URI.encode_www_form_component(airport_id)}/weather/taf")
135
+ end
136
+
137
+ # =========================================================================
138
+ # Facility Data Endpoints
139
+ # =========================================================================
140
+
141
+ # Get facility information for a specific airport
142
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
143
+ # @return [Hash] Facility information for the airport
144
+ def get_facilities(airport_id)
145
+ get("/airports/#{URI.encode_www_form_component(airport_id)}/facilities")
146
+ end
147
+
148
+ # Get nearby airports within a specified radius
149
+ # @param airport_id [String] Airport identifier (e.g., 'KIAD')
150
+ # @param radius [Float] Search radius in nautical miles (default: 20, max: 100)
151
+ # @param options [Hash] Additional filter options
152
+ # @option options [String] :facility_use Filter by facility use ('PUBLIC', 'PRIVATE', 'MILITARY')
153
+ # @return [Hash] List of nearby airports
154
+ def get_nearby_airports(airport_id, radius = 20, options = {})
155
+ params = { radius: validate_range(radius, 0.1, 100, 'radius') }
156
+
157
+ if options[:facility_use]
158
+ params[:facilityUse] = validate_enum(options[:facility_use], %w[PUBLIC PRIVATE MILITARY], 'facility_use')
159
+ end
160
+
161
+ get("/airports/#{URI.encode_www_form_component(airport_id)}/nearby", params)
162
+ end
163
+
164
+ # =========================================================================
165
+ # Helper Methods
166
+ # =========================================================================
167
+
168
+ private
169
+
170
+ # Make a GET request to the ADIP API
171
+ # @param path [String] API endpoint path
172
+ # @param params [Hash] Query parameters
173
+ # @return [Hash] Parsed JSON response
174
+ def get(path, params = {})
175
+ uri = URI.parse("#{BASE_URL}#{path}")
176
+ uri.query = URI.encode_www_form(params) if params.any?
177
+
178
+ http = Net::HTTP.new(uri.host, uri.port)
179
+ http.use_ssl = true
180
+
181
+ request = Net::HTTP::Get.new(uri)
182
+ request['client_id'] = @client_id
183
+ request['client_secret'] = @client_secret
184
+ request['Accept'] = 'application/json'
185
+
186
+ response = http.request(request)
187
+ handle_response(response)
188
+ end
189
+
190
+ # Handle API response
191
+ # @param response [Net::HTTPResponse] HTTP response from the API
192
+ # @return [Hash] Parsed JSON response
193
+ # @raise [RuntimeError] If the response is not successful
194
+ def handle_response(response)
195
+ case response.code.to_i
196
+ when 200
197
+ JSON.parse(response.body)
198
+ when 400
199
+ raise "Bad request: #{response.body}"
200
+ when 401
201
+ raise "Unauthorized: Invalid API credentials"
202
+ when 404
203
+ raise "Not found: Requested resource not available"
204
+ when 500
205
+ raise "Internal server error: #{response.body}"
206
+ else
207
+ raise "Unexpected response (#{response.code}): #{response.body}"
208
+ end
209
+ end
210
+
211
+ # Validate that a value is within a specific range
212
+ # @param value [Numeric] Value to validate
213
+ # @param min [Numeric] Minimum allowed value
214
+ # @param max [Numeric] Maximum allowed value (or nil for no upper limit)
215
+ # @param param_name [String] Parameter name for error messages
216
+ # @return [Numeric] Validated value
217
+ # @raise [ArgumentError] If value is not within range
218
+ def validate_range(value, min, max, param_name)
219
+ value = value.to_f
220
+ if value < min || (max && value > max)
221
+ max_msg = max ? " and #{max}" : ""
222
+ raise ArgumentError, "Invalid #{param_name}. Must be between #{min}#{max_msg}."
223
+ end
224
+ value
225
+ end
226
+
227
+ # Validate that a value is within an enumeration
228
+ # @param value [String] Value to validate
229
+ # @param allowed_values [Array<String>] Allowed values
230
+ # @param param_name [String] Parameter name for error messages
231
+ # @return [String] Validated value
232
+ # @raise [ArgumentError] If value is not in allowed values
233
+ def validate_enum(value, allowed_values, param_name)
234
+ value = value.to_s.upcase
235
+ unless allowed_values.include?(value)
236
+ raise ArgumentError, "Invalid #{param_name}. Must be one of: #{allowed_values.join(', ')}"
237
+ end
238
+ value
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FaaAdip
4
+ VERSION = "0.1.0"
5
+ end
data/lib/faa_adip.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "faa_adip/version"
4
+ require_relative "faa_adip/service"
5
+
6
+ # FaaAdip - Ruby gem to interact with FAA's Airport Data & Information Portal (ADIP) API
7
+ # https://external-api.faa.gov/adip
8
+ #
9
+ # This gem provides access to FAA's ADIP API, which contains information about airports,
10
+ # including facilities, runways, and other airport data.
11
+ module FaaAdip
12
+ class Error < StandardError; end
13
+
14
+ # Convenience method to create a new service instance
15
+ # @param client_id [String] FAA API client ID
16
+ # @param client_secret [String] FAA API client secret
17
+ # @return [FaaAdip::Service] A new service instance
18
+ def self.new(client_id = nil, client_secret = nil)
19
+ Service.new(client_id, client_secret)
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: raad-adip
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - JP Silvashy
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-05-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: bundler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rake
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '13.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '13.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rubocop
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.21'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.21'
68
+ - !ruby/object:Gem::Dependency
69
+ name: yard
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.9'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.9'
82
+ - !ruby/object:Gem::Dependency
83
+ name: webmock
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.14'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.14'
96
+ - !ruby/object:Gem::Dependency
97
+ name: vcr
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '6.0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '6.0'
110
+ description: A Ruby gem to interact with the FAA's ADIP API, which provides access
111
+ to airport information, runways, facilities, and weather data.
112
+ email:
113
+ - jp@raad.com
114
+ - jpsilvashy@gmail.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - CHANGELOG.md
120
+ - LICENSE
121
+ - README.md
122
+ - lib/faa_adip.rb
123
+ - lib/faa_adip/service.rb
124
+ - lib/faa_adip/version.rb
125
+ homepage: https://github.com/raad-labs/adip
126
+ licenses:
127
+ - MIT
128
+ metadata:
129
+ homepage_uri: https://github.com/raad-labs/adip
130
+ source_code_uri: https://github.com/raad-labs/adip
131
+ changelog_uri: https://github.com/raad-labs/adip/blob/main/CHANGELOG.md
132
+ documentation_uri: https://www.rubydoc.info/gems/raad-adip
133
+ allowed_push_host: https://rubygems.org
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: 2.6.0
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.6.3
149
+ specification_version: 4
150
+ summary: Ruby wrapper for the FAA Airport Data & Information Portal (ADIP) API
151
+ test_files: []