Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# GitHub Copilot Instructions

- CAREFULLY read all instructions before coding.
- Spend as much time as needed to analyze and understand the context.
- Check online references/documentation if applicable.
- https://developer-en.vnnox.com/
- API example response also availble in the docs
- ALWAYS follow best practices and coding standards for public ruby gems.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@

# rspec failure tracking
.rspec_status

/docs/internal
8 changes: 7 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
AllCops:
TargetRubyVersion: 3.2
TargetRubyVersion: 2.7
NewCops: enable

Metrics/BlockLength:
Exclude:
- "spec/**/*"
- "novacloud_client.gemspec"

Style/StringLiterals:
EnforcedStyle: double_quotes
Expand Down
11 changes: 7 additions & 4 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ source "https://rubygems.org"
gemspec

gem "irb"
gem "rake", "~> 13.0"

gem "rspec", "~> 3.0"

gem "rubocop", "~> 1.21"
group :development, :test do
gem "rake", "~> 13.0"
gem "rspec", "~> 3.12"
gem "rubocop", "~> 1.60", require: false
gem "webmock", "~> 3.19"
gem "yard", "~> 0.9"
end
115 changes: 115 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
PATH
remote: .
specs:
novacloud_client (0.1.0)
faraday (~> 2.7)

GEM
remote: https://rubygems.org/
specs:
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
bigdecimal (3.3.1)
crack (1.0.1)
bigdecimal
rexml
date (3.4.1)
diff-lcs (1.6.2)
erb (5.1.1)
faraday (2.14.0)
faraday-net_http (>= 2.0, < 3.5)
json
logger
faraday-net_http (3.4.1)
net-http (>= 0.5.0)
hashdiff (1.2.1)
io-console (0.8.1)
irb (1.15.2)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
json (2.15.2)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
net-http (0.6.0)
uri
parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1)
racc
pp (0.6.3)
prettyprint
prettyprint (0.2.0)
prism (1.6.0)
psych (5.2.6)
date
stringio
public_suffix (6.0.2)
racc (1.8.1)
rainbow (3.1.1)
rake (13.3.0)
rdoc (6.15.0)
erb
psych (>= 4.0.0)
tsort
regexp_parser (2.11.3)
reline (0.6.2)
io-console (~> 0.5)
rexml (3.4.4)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.6)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.6)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.6)
rubocop (1.81.6)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.47.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.47.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
ruby-progressbar (1.13.0)
stringio (3.1.7)
tsort (0.2.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.1.0)
uri (1.0.4)
webmock (3.25.2)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
yard (0.9.37)

PLATFORMS
arm64-darwin-23
ruby

DEPENDENCIES
irb
novacloud_client!
rake (~> 13.0)
rspec (~> 3.12)
rubocop (~> 1.60)
webmock (~> 3.19)
yard (~> 0.9)

BUNDLED WITH
2.7.1
51 changes: 24 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
# NovacloudClient
# NovaCloud Client (WIP)

TODO: Delete this and the text below, and describe your gem
Sprint 01 delivered the core HTTP client for the NovaCloud Open Platform. The gem now:

Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/novacloud_client`. To experiment with that code, run `bin/console` for an interactive prompt.
- Manages configuration once (`app_key`, `app_secret`, `service_domain`).
- Handles authentication headers automatically via Faraday middleware.
- Maps HTTP errors to a typed exception hierarchy.
- Normalizes GET/POST payloads and parses JSON responses.

## Installation
## Quick Start

TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
```ruby
require "novacloud_client"

Install the gem and add to the application's Gemfile by executing:
client = NovacloudClient::Client.new(
app_key: "YOUR_APP_KEY",
app_secret: "YOUR_APP_SECRET",
service_domain: "open-us.vnnox.com"
)

```bash
bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
response = client.request(
http_method: :get,
endpoint: "/v2/player/list",
params: { start: 0, count: 20 }
)
```

If bundler is not being used to manage dependencies, install the gem by executing:
### Development

```bash
gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
bundle install
bundle exec rspec
bundle exec rubocop
```

## Usage

TODO: Write usage instructions here

## Development

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.

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).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/novacloud_client.

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
Sprint 02 will add resource helpers (e.g., `client.players.list`) and response objects built on these foundations.
11 changes: 6 additions & 5 deletions lib/novacloud_client.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# frozen_string_literal: true

require_relative "novacloud_client/version"
require "faraday"
require "json"

module NovacloudClient
class Error < StandardError; end
# Your code goes here...
end
require_relative "novacloud_client/version"
require_relative "novacloud_client/errors"
require_relative "novacloud_client/configuration"
require_relative "novacloud_client/client"
80 changes: 80 additions & 0 deletions lib/novacloud_client/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# frozen_string_literal: true

require "faraday"
require "json"

require_relative "errors"
require_relative "configuration"
require_relative "middleware/authentication"
require_relative "middleware/error_handler"

module NovacloudClient
# Central entry point for interacting with the NovaCloud API.
class Client
attr_reader :config

def initialize(app_key:, app_secret:, service_domain:, &faraday_block)
@config = Configuration.new
@config.app_key = app_key
@config.app_secret = app_secret
@config.service_domain = service_domain
@config.validate!

@faraday_block = faraday_block
end

def connection
@connection ||= build_connection
end

def request(http_method:, endpoint:, params: {})
symbolized_method = http_method.to_sym
response = connection.public_send(symbolized_method) do |req|
req.url endpoint
apply_request_payload(req, symbolized_method, params)
end

parse_body(response)
end

private

def build_connection
Faraday.new(url: config.base_url) do |faraday|
faraday.headers["Accept"] = "application/json"

faraday.use Middleware::Authentication,
app_key: config.app_key,
app_secret: config.app_secret
faraday.use Middleware::ErrorHandler

@faraday_block&.call(faraday)

faraday.adapter config.adapter
end
end

def apply_request_payload(request, http_method, params)
return if params.empty?

case http_method
when :get, :delete
request.params.update(params)
else
request.headers["Content-Type"] = "application/json; charset=utf-8"
request.body = JSON.generate(params)
end
end

def parse_body(response)
body = response.body
return nil if body.nil?
return body unless body.is_a?(String)
return nil if body.strip.empty?

JSON.parse(body)
rescue JSON::ParserError
body
end
end
end
31 changes: 31 additions & 0 deletions lib/novacloud_client/configuration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module NovacloudClient
# Holds configuration for a NovacloudClient::Client instance.
class Configuration
attr_accessor :app_key, :app_secret, :service_domain, :adapter

def initialize
@adapter = :net_http
end

def base_url
"https://#{service_domain}"
end

def validate!
raise ArgumentError, "app_key is required" if blank?(app_key)
raise ArgumentError, "app_secret is required" if blank?(app_secret)
raise ArgumentError, "service_domain is required" if blank?(service_domain)
end

private

def blank?(value)
return true if value.nil?
return value.strip.empty? if value.is_a?(String)

value.respond_to?(:empty?) ? value.empty? : false
end
end
end
26 changes: 26 additions & 0 deletions lib/novacloud_client/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module NovacloudClient
# Base error class allowing access to the Faraday environment.
class Error < StandardError
attr_reader :response

def initialize(message = nil, response: nil)
super(message)
@response = response
end
end

class ClientError < Error; end
class BadRequestError < ClientError; end
class AuthenticationError < ClientError; end
class PermissionError < ClientError; end
class NotAcceptableError < ClientError; end
class RateLimitError < ClientError; end

class ServerError < Error; end
class InternalServerError < ServerError; end
class BadGatewayError < ServerError; end
class ServiceUnavailableError < ServerError; end
class GatewayTimeoutError < ServerError; end
end
Loading