Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add measurements #21

Merged
merged 22 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
### 0.2.1 (Next)

* Your contribution here.
* [#20](https://github.com/dblock/open-weather-ruby-client/pull/20), [#19](https://github.com/dblock/open-weather-ruby-client/pull/19), [#18](https://github.com/dblock/open-weather-ruby-client/pull/18): Added support for Stations API - [@wasabigeek](https://github.com/wasabigeek).
* [#21](https://github.com/dblock/open-weather-ruby-client/pull/21), [#20](https://github.com/dblock/open-weather-ruby-client/pull/20), [#19](https://github.com/dblock/open-weather-ruby-client/pull/19), [#18](https://github.com/dblock/open-weather-ruby-client/pull/18): Added support for Stations API - [@wasabigeek](https://github.com/wasabigeek).

### 0.2.0 (2020/05/17)

Expand Down
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Unlike other clients, including [open-weather](https://github.com/coderhs/ruby_o
- [Get Station](#get-station)
- [Update Station](#update-station)
- [Delete Station](#delete-station)
- [Create Measurements](#create-measurements)
- [Get Measurements](#get-measurements)
- [Configuration](#configuration)
- [Units](#units)
- [Converting Temperature](#converting-temperature)
Expand Down Expand Up @@ -253,6 +255,42 @@ To delete a station, call the client method:
data = client.delete_station('5ed2118acca8ce0001f1aeg1') # => nil
```

#### Create Measurements

To create measurements, call the client method:
```ruby
client.create_measurements([
{
"station_id": -1,
"dt": 1479817340,
"temperature": 18.7,
"wind_speed": 1.2,
"wind_gust": 3.4,
"pressure": 1021,
"humidity": 87,
"rain_1h": 2,
"clouds": [
{
"condition": 'NSC'
}
]
}
]) # => nil
```

#### Get Measurements

To get measurements, call the client method with the mandatory parameters:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mandatory -> required

Doesn't look like all these are required, so I'd say "The following parameters are required: ...".

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are apparently all required, unfortunately >_< https://openweathermap.org/stations#get_measurements

```ruby
client.get_measurements(
station_id: '5ed21a12cca8ce0001f1aef1',
type: 'd',
limit: 100,
from: 1469817340,
to: 1591620047
) # => Array[Hash]
wasabigeek marked this conversation as resolved.
Show resolved Hide resolved
```

## Configuration

You can configure client options, globally.
Expand Down
13 changes: 13 additions & 0 deletions lib/open_weather/endpoints/stations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ def delete_station(id)
nil
end

def create_measurements(measurements, options = {})
post('measurements', options.merge(body: measurements))
nil
wasabigeek marked this conversation as resolved.
Show resolved Hide resolved
end

def get_measurements(options)
required_keys = %i[station_id type limit from to]
missing_keys = required_keys - options.keys
raise ArgumentError, "Missing params: #{missing_keys.join(', ')}" unless missing_keys.empty?
wasabigeek marked this conversation as resolved.
Show resolved Hide resolved

get('measurements', options)
end

private

def validate_id(id)
Expand Down
14 changes: 13 additions & 1 deletion lib/open_weather/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ def delete(path, options = {})

private

#
# @param [Symbol] method - Faraday HTTP method.
# @param [String] path - URL to send.
# @param [Hash] options - :appid, :lang, :units, :endpoint, :body keys will configure the request.
# The rest will be converted to query params for GET/DELETE, or jsonified for POST/PUT.
#
# @return [Object] - the Faraday::Response#body.
#
def request(method, path, options)
options = options.dup
options[:appid] ||= api_key if api_key.present?
Expand All @@ -34,7 +42,11 @@ def request(method, path, options)
when :post, :put
request.path = path
request.params = { appid: options.delete(:appid) }
request.body = options.to_json unless options.empty?
if options.key?(:body)
request.body = options.delete(:body).to_json
elsif !options.empty? # is a Hash, so `.present?` doesn't capture the same behaviour
wasabigeek marked this conversation as resolved.
Show resolved Hide resolved
request.body = options.to_json
end
end
request.options.merge!(options.delete(:request)) if options.key?(:request)
end
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions spec/fixtures/open_weather/stations/create_measurement_success.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions spec/fixtures/open_weather/stations/get_measurement_success.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions spec/open_weather/endpoints/stations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,68 @@
end
end
end

describe '#create_measurements' do
it 'creates measurements', vcr: { cassette_name: 'stations/create_measurement_success' } do
create_params = {
"station_id": '5ed21a12cca8ce0001f1aef1',
"dt": 1479817340,
"temperature": 18.7,
"wind_speed": 1.2,
"wind_gust": 3.4,
"pressure": 1021,
"humidity": 87,
"rain_1h": 2,
"clouds": [
{
"condition": 'NSC'
}
]
}
data = client.create_measurements([create_params])
expect(data).to be_nil
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to make sure the API was called, expect a POST to occur, and_call_original to enable the VCR trace

Copy link
Collaborator Author

@wasabigeek wasabigeek Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, we do this because the nil return is more likely to hide a false positive?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely

end

context 'when station does not exist' do
it 'raises error', vcr: { cassette_name: 'stations/create_measurement_failed_with_invalid_station' } do
create_params = {
"station_id": -1,
"dt": 1479817340,
"temperature": 18.7,
"wind_speed": 1.2,
"wind_gust": 3.4,
"pressure": 1021,
"humidity": 87,
"rain_1h": 2,
"clouds": [
{
"condition": 'NSC'
}
]
}
expect { client.create_measurements([create_params]) }
.to raise_error(OpenWeather::Errors::Fault, /expected=string, got=number/)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd error, doesn't tell me that the station was invalid? Should this be a specialized error?

Copy link
Collaborator Author

@wasabigeek wasabigeek Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be what the API returns, at least with a number:
https://github.com/dblock/open-weather-ruby-client/pull/21/files#diff-9b98e6c2b2b1880dacb9ff685ba9c676R41

Let me try with a string

EDIT: You were right, good catch

end
end
end

describe '#get_measurements' do
it 'gets measurements', vcr: { cassette_name: 'stations/get_measurement_success' } do
data = client.get_measurements(
station_id: '5ed21a12cca8ce0001f1aef1',
type: 'd',
limit: 100,
from: 1469817340,
to: 1591620047
)
expect(data.size).to eq(1)
expect(data.first['station_id']).to eq('5ed21a12cca8ce0001f1aef1')
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the request / response structures for measurements are somewhat different e.g. wind_speed vs wind: { speed: ... }, I've left it as a raw hash for now. I could make it into a Hashie I suppose?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should make a new structure for anything unique.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There would potentially be two structures then, so OpenWeather::Stations::MeasurementRequest / MeasurementResponse? I could try to normalize the differences, but the response isn't as well documented as the request :/

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should make one, Measurement that would match the response since it's more frequently queried I imagine than sent, and convert into the right hash for requests for the OO model.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a best effort one :/ I guessed the properties for some of the "submodels" based on personal testing, but there are some weird ones e.g. I created a measurement with wind_gust and wind_speed, but in the response wind is an empty hash. This makes a OO type model a bit tricky.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine. You should double-check whether such documentation is really missing on their site, then send them an email asking to document the structures.

end

context 'without mandatory params' do
wasabigeek marked this conversation as resolved.
Show resolved Hide resolved
it 'raises error' do
expect { client.get_measurements(something: 'something') }.to raise_error(ArgumentError, /station_id, type, limit, from, to/)
end
end
end
end