mirror of
https://github.com/internetee/registry.git
synced 2025-06-11 15:14:47 +02:00
Add handling of type filters and handling of ETags
Add API documentation and test cases around ETags for the API.
This commit is contained in:
parent
960e4084e3
commit
6e5a97ad4d
5 changed files with 182 additions and 19 deletions
|
@ -2,10 +2,14 @@ module Repp
|
||||||
module V1
|
module V1
|
||||||
class RetainedDomainsController < ActionController::API
|
class RetainedDomainsController < ActionController::API
|
||||||
def index
|
def index
|
||||||
domains = RetainedDomains.new
|
domains = RetainedDomains.new(query_params)
|
||||||
|
|
||||||
render json: { count: domains.count, domains: domains.to_jsonable }
|
render json: { count: domains.count, domains: domains.to_jsonable }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def query_params
|
||||||
|
params.permit(:type)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,26 +4,55 @@ class RetainedDomains
|
||||||
RESERVED = 'reserved'.freeze
|
RESERVED = 'reserved'.freeze
|
||||||
BLOCKED = 'blocked'.freeze
|
BLOCKED = 'blocked'.freeze
|
||||||
|
|
||||||
attr_reader :domains
|
attr_reader :domains,
|
||||||
|
:type
|
||||||
|
|
||||||
def initialize
|
def initialize(params)
|
||||||
|
@type = establish_type(params)
|
||||||
@domains = gather_domains
|
@domains = gather_domains
|
||||||
end
|
end
|
||||||
|
|
||||||
def gather_domains
|
delegate :count, to: :domains
|
||||||
blocked_domains = BlockedDomain.order(name: :desc).all
|
|
||||||
reserved_domains = ReservedDomain.order(name: :desc).all
|
|
||||||
|
|
||||||
|
def to_jsonable
|
||||||
|
domains.map { |el| domain_to_jsonable(el) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def establish_type(params)
|
||||||
|
type = params[:type]
|
||||||
|
|
||||||
|
case type
|
||||||
|
when 'reserved' then :reserved
|
||||||
|
when 'blocked' then :blocked
|
||||||
|
else :all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def gather_domains
|
||||||
domains = blocked_domains.to_a.union(reserved_domains.to_a)
|
domains = blocked_domains.to_a.union(reserved_domains.to_a)
|
||||||
|
|
||||||
domains.sort_by(&:name)
|
domains.sort_by(&:name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_jsonable
|
def blocked_domains
|
||||||
domains.map { |el| domain_to_json(el) }
|
if %i[all blocked].include?(type)
|
||||||
|
BlockedDomain.order(name: :desc).all
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def domain_to_json(domain)
|
def reserved_domains
|
||||||
|
if %i[all reserved].include?(type)
|
||||||
|
ReservedDomain.order(name: :desc).all
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def domain_to_jsonable(domain)
|
||||||
status = case domain
|
status = case domain
|
||||||
when ReservedDomain then RESERVED
|
when ReservedDomain then RESERVED
|
||||||
when BlockedDomain then BLOCKED
|
when BlockedDomain then BLOCKED
|
||||||
|
@ -37,6 +66,4 @@ class RetainedDomains
|
||||||
punycode_name: punycode,
|
punycode_name: punycode,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
delegate :count, to: :domains
|
|
||||||
end
|
end
|
||||||
|
|
96
doc/repp/v1/retained_domains.md
Normal file
96
doc/repp/v1/retained_domains.md
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
## GET /repp/v1/retained_domains
|
||||||
|
|
||||||
|
Return a list of reserved and blocked domains, along with total count. You can
|
||||||
|
filter them by type of the domain, which can be either reserved or blocked.
|
||||||
|
|
||||||
|
In contrast with other endpoints in REPP, this one is publicly available for
|
||||||
|
anyone without authentication.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
| Field name | Required | Type | Allowed values | Description |
|
||||||
|
| ---------- | -------- | ---- | -------------- | ----------- |
|
||||||
|
| type | false | string | ["reserved", "blocked"] | Type of domains to show |
|
||||||
|
|
||||||
|
|
||||||
|
#### Request
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /repp/v1/retained_domains?type=reserved HTTP/1.1
|
||||||
|
Accept: application/json
|
||||||
|
User-Agent: curl/7.64.1
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Response
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Date: Fri, 15 May 2020 11:30:07 GMT
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
ETag: W/"a905b531243a6b0be42beb9d6ce60619"
|
||||||
|
Cache-Control: max-age=0, private, must-revalidate
|
||||||
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"domains": [
|
||||||
|
{
|
||||||
|
"name": "reserved.test",
|
||||||
|
"status": "reserved",
|
||||||
|
"punycode_name": "reserved.test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After you have made the first request, you can save the ETag header, and
|
||||||
|
send it as If-None-Match in the subsequent request for cache validation.
|
||||||
|
Due to the fact that the lists are not changing frequently and are quite long,
|
||||||
|
it is recommended that you take advantage of ETag cache.
|
||||||
|
|
||||||
|
ETag key values depend on the request parameters. A request for only blocked
|
||||||
|
domains returns different cache key than request for all domains.
|
||||||
|
|
||||||
|
### Cache Request
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /repp/v1/retained_domains?type=reserved HTTP/1.1
|
||||||
|
Accept: application/json
|
||||||
|
User-Agent: curl/7.64.1
|
||||||
|
If-None-Match: W/"a905b531243a6b0be42beb9d6ce60619"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache hit response
|
||||||
|
|
||||||
|
Response with no body and status 304 is sent in case the list have not changed.
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP/1.1 304 Not Modified
|
||||||
|
Date: Fri, 15 May 2020 11:34:25 GMT
|
||||||
|
ETag: W/"a905b531243a6b0be42beb9d6ce60619"
|
||||||
|
Cache-Control: max-age=0, private, must-revalidate
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache miss response
|
||||||
|
|
||||||
|
Standard 200 response is sent when the list have changed since last requested.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Date: Fri, 15 May 2020 11:30:07 GMT
|
||||||
|
Content-Type: application/json; charset=utf-8
|
||||||
|
ETag: W/"a905b531243a6b0be42beb9d6ce60619"
|
||||||
|
Transfer-Encoding: chunked
|
||||||
|
|
||||||
|
{
|
||||||
|
"count": 1,
|
||||||
|
"domains": [
|
||||||
|
{
|
||||||
|
"name": "reserved.test",
|
||||||
|
"status": "reserved",
|
||||||
|
"punycode_name": "reserved.test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
|
@ -1,7 +1,7 @@
|
||||||
# REPP integration specification
|
# REPP integration specification
|
||||||
|
|
||||||
REPP uses HTTP/1.1 protocol (http://tools.ietf.org/html/rfc2616) and
|
REPP uses HTTP/1.1 protocol (http://tools.ietf.org/html/rfc2616) and
|
||||||
Basic Authentication (http://tools.ietf.org/html/rfc2617#section-2) using
|
Basic Authentication (http://tools.ietf.org/html/rfc2617#section-2) using
|
||||||
Secure Transport (https://tools.ietf.org/html/rfc5246) with certificate and key (https://tools.ietf.org/html/rfc5280).
|
Secure Transport (https://tools.ietf.org/html/rfc5246) with certificate and key (https://tools.ietf.org/html/rfc5280).
|
||||||
|
|
||||||
Credentials and certificate are issued by EIS (in an exchange for desired API username, CSR and IP).
|
Credentials and certificate are issued by EIS (in an exchange for desired API username, CSR and IP).
|
||||||
|
@ -10,13 +10,14 @@ To quickly test the API, use curl:
|
||||||
|
|
||||||
curl -q -k --cert user.crt.pem --key user.key.pem https://TBA/repp/v1/accounts/balance -u username:password
|
curl -q -k --cert user.crt.pem --key user.key.pem https://TBA/repp/v1/accounts/balance -u username:password
|
||||||
|
|
||||||
Test API endpoint: https://testepp.internet.ee/repp/v1
|
Test API endpoint: https://testepp.internet.ee/repp/v1
|
||||||
Production API endpoint: TBA
|
Production API endpoint: TBA
|
||||||
|
|
||||||
Main communication specification through Restful EPP (REPP):
|
Main communication specification through Restful EPP (REPP):
|
||||||
|
|
||||||
[Contact related functions](repp/v1/contact.md)
|
[Contact related functions](repp/v1/contact.md)
|
||||||
[Domain related functions](repp/v1/domain.md)
|
[Domain related functions](repp/v1/domain.md)
|
||||||
[Domain transfers](repp/v1/domain_transfers.md)
|
[Domain transfers](repp/v1/domain_transfers.md)
|
||||||
[Account related functions](repp/v1/account.md)
|
[Account related functions](repp/v1/account.md)
|
||||||
[Nameservers](repp/v1/nameservers.md)
|
[Nameservers](repp/v1/nameservers.md)
|
||||||
|
[Retained domains](repp/v1/retained_domains.md)
|
||||||
|
|
|
@ -24,6 +24,41 @@ class ReppV1RetainedDomainsTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal response_json[:domains], expected_objects
|
assert_equal response_json[:domains], expected_objects
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_get_index_with_type_parameter
|
||||||
|
get repp_v1_retained_domains_path({ 'type' => 'reserved' })
|
||||||
|
response_json = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
|
||||||
|
assert response_json[:count] == 1
|
||||||
|
|
||||||
|
expected_objects = [{ name: 'reserved.test',
|
||||||
|
status: 'reserved',
|
||||||
|
punycode_name: 'reserved.test' }]
|
||||||
|
|
||||||
|
assert_equal response_json[:domains], expected_objects
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_etags_cache
|
||||||
|
get repp_v1_retained_domains_path({ 'type' => 'reserved' })
|
||||||
|
etag = response.headers['ETag']
|
||||||
|
|
||||||
|
get repp_v1_retained_domains_path({ 'type' => 'reserved' }),
|
||||||
|
headers: { 'If-None-Match' => etag }
|
||||||
|
|
||||||
|
assert_equal response.status, 304
|
||||||
|
assert_equal response.body, ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_etags_cache_valid_for_type_only
|
||||||
|
get repp_v1_retained_domains_path({ 'type' => 'blocked' })
|
||||||
|
etag = response.headers['ETag']
|
||||||
|
|
||||||
|
get repp_v1_retained_domains_path, headers: { 'If-None-Match' => etag }
|
||||||
|
|
||||||
|
assert_equal response.status, 200
|
||||||
|
response_json = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
assert response_json[:count] == 3
|
||||||
|
end
|
||||||
|
|
||||||
def test_cors_preflight
|
def test_cors_preflight
|
||||||
process :options, repp_v1_retained_domains_path, headers: { 'Origin' => 'https://example.com' }
|
process :options, repp_v1_retained_domains_path, headers: { 'Origin' => 'https://example.com' }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue