ecfr 1.0.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 +7 -0
- data/.circleci/config.yml +11 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rspec_parallel +4 -0
- data/.rubocop.yml +28 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +6 -0
- data/Dockerfile +6 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +138 -0
- data/LICENSE.txt +21 -0
- data/README.md +133 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/ecfr.gemspec +74 -0
- data/lib/ecfr/admin_service/agency/hierarchy.rb +27 -0
- data/lib/ecfr/admin_service/agency.rb +34 -0
- data/lib/ecfr/admin_service/api_documentation.rb +7 -0
- data/lib/ecfr/admin_service/base.rb +26 -0
- data/lib/ecfr/admin_service/build.rb +38 -0
- data/lib/ecfr/admin_service/ecfr_correction/cfr_reference.rb +17 -0
- data/lib/ecfr/admin_service/ecfr_correction.rb +78 -0
- data/lib/ecfr/admin_service/editorial_note/hierarchy.rb +19 -0
- data/lib/ecfr/admin_service/editorial_note.rb +40 -0
- data/lib/ecfr/admin_service/ibr_cfr_range/address.rb +17 -0
- data/lib/ecfr/admin_service/ibr_cfr_range/organization.rb +28 -0
- data/lib/ecfr/admin_service/ibr_cfr_range.rb +67 -0
- data/lib/ecfr/admin_service/issue/change.rb +19 -0
- data/lib/ecfr/admin_service/issue.rb +86 -0
- data/lib/ecfr/admin_service/site_notification.rb +34 -0
- data/lib/ecfr/admin_service/status.rb +7 -0
- data/lib/ecfr/attribute_caster.rb +72 -0
- data/lib/ecfr/attribute_method_definition.rb +92 -0
- data/lib/ecfr/base.rb +71 -0
- data/lib/ecfr/client.rb +318 -0
- data/lib/ecfr/common/hierarchy.rb +35 -0
- data/lib/ecfr/configuration.rb +58 -0
- data/lib/ecfr/constants.rb +21 -0
- data/lib/ecfr/default_documentation_setup.rb +39 -0
- data/lib/ecfr/default_status_setup.rb +46 -0
- data/lib/ecfr/diff_service/base.rb +17 -0
- data/lib/ecfr/diff_service/status.rb +34 -0
- data/lib/ecfr/extensible.rb +45 -0
- data/lib/ecfr/facet_attribute_method_definition.rb +47 -0
- data/lib/ecfr/faraday/user_agent/middleware.rb +14 -0
- data/lib/ecfr/ofr_profile_service/base.rb +20 -0
- data/lib/ecfr/ofr_profile_service/status.rb +7 -0
- data/lib/ecfr/parallel_client.rb +33 -0
- data/lib/ecfr/prince_xml_service/base.rb +17 -0
- data/lib/ecfr/prince_xml_service/pdf.rb +31 -0
- data/lib/ecfr/renderer_service/base.rb +31 -0
- data/lib/ecfr/renderer_service/content.rb +34 -0
- data/lib/ecfr/renderer_service/diff.rb +31 -0
- data/lib/ecfr/renderer_service/origin.rb +56 -0
- data/lib/ecfr/renderer_service/status.rb +7 -0
- data/lib/ecfr/request_representation.rb +12 -0
- data/lib/ecfr/search_service/api_documentation.rb +7 -0
- data/lib/ecfr/search_service/base.rb +23 -0
- data/lib/ecfr/search_service/content_version/count.rb +33 -0
- data/lib/ecfr/search_service/content_version/hierarchical_count.rb +17 -0
- data/lib/ecfr/search_service/content_version/hierarchical_count_node.rb +30 -0
- data/lib/ecfr/search_service/content_version/hierarchichal_result.rb +42 -0
- data/lib/ecfr/search_service/content_version/result.rb +110 -0
- data/lib/ecfr/search_service/content_version/suggestion.rb +76 -0
- data/lib/ecfr/search_service/content_version/summary.rb +27 -0
- data/lib/ecfr/search_service/content_version.rb +85 -0
- data/lib/ecfr/search_service/date_facet.rb +19 -0
- data/lib/ecfr/search_service/facet_base.rb +55 -0
- data/lib/ecfr/search_service/status.rb +7 -0
- data/lib/ecfr/search_service/title_facet.rb +18 -0
- data/lib/ecfr/subscriptions_service/base.rb +19 -0
- data/lib/ecfr/subscriptions_service/status.rb +7 -0
- data/lib/ecfr/subscriptions_service/subscription.rb +97 -0
- data/lib/ecfr/testing/extensions/admin_service/ecfr_correction_extensions.rb +13 -0
- data/lib/ecfr/testing/extensions/admin_service/issue_extensions.rb +13 -0
- data/lib/ecfr/testing/extensions/renderer_service/origin_extensions.rb +13 -0
- data/lib/ecfr/testing/extensions/search_service/content_version_result_extensions.rb +16 -0
- data/lib/ecfr/testing/extensions/search_service/date_facet_extensions.rb +13 -0
- data/lib/ecfr/testing/extensions/versioner_service/ancestors_extensions.rb +20 -0
- data/lib/ecfr/testing/extensions/versioner_service/title_extenstions.rb +16 -0
- data/lib/ecfr/testing/factories/admin_service/cfr_reference_factory.rb +14 -0
- data/lib/ecfr/testing/factories/admin_service/ecfr_correction_factory.rb +31 -0
- data/lib/ecfr/testing/factories/admin_service/issue_change_factory.rb +12 -0
- data/lib/ecfr/testing/factories/admin_service/issue_factory.rb +21 -0
- data/lib/ecfr/testing/factories/common/hierarchy_factory.rb +36 -0
- data/lib/ecfr/testing/factories/renderer_service/origin_factory.rb +32 -0
- data/lib/ecfr/testing/factories/search_service/content_version_count_factory.rb +20 -0
- data/lib/ecfr/testing/factories/search_service/content_version_result_factory.rb +76 -0
- data/lib/ecfr/testing/factories/search_service/date_facet_factory.rb +12 -0
- data/lib/ecfr/testing/factories/versioner_service/ancestors_factory.rb +26 -0
- data/lib/ecfr/testing/factories/versioner_service/metadata_node_info_factory.rb +15 -0
- data/lib/ecfr/testing/factories/versioner_service/node_summary_factory.rb +16 -0
- data/lib/ecfr/testing/factories/versioner_service/structure_factory.rb +57 -0
- data/lib/ecfr/testing/factories/versioner_service/title_factory.rb +36 -0
- data/lib/ecfr/testing/factory_bot_helpers/content_version.rb +38 -0
- data/lib/ecfr/testing/factory_bot_helpers/ecfr_gem_initialize_helpers.rb +51 -0
- data/lib/ecfr/testing/helpers/response_helper.rb +5 -0
- data/lib/ecfr/testing/strategies/ecfr_attribute_hash_strategy.rb +37 -0
- data/lib/ecfr/testing.rb +28 -0
- data/lib/ecfr/version.rb +5 -0
- data/lib/ecfr/versioner_service/ancestors/metadata_node_info.rb +22 -0
- data/lib/ecfr/versioner_service/ancestors/node_summary.rb +54 -0
- data/lib/ecfr/versioner_service/ancestors.rb +152 -0
- data/lib/ecfr/versioner_service/api_documentation.rb +7 -0
- data/lib/ecfr/versioner_service/base.rb +24 -0
- data/lib/ecfr/versioner_service/status.rb +7 -0
- data/lib/ecfr/versioner_service/structure.rb +120 -0
- data/lib/ecfr/versioner_service/title.rb +78 -0
- data/lib/ecfr/versioner_service/xml_content.rb +59 -0
- data/lib/ecfr.rb +90 -0
- data/lib/yard/attribute_handler.rb +87 -0
- data/lib/yard/metadata_handler.rb +87 -0
- metadata +389 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module ParallelClient
|
|
3
|
+
module ClassMethods
|
|
4
|
+
def parallel_client(base_url:, client_options: {})
|
|
5
|
+
client(
|
|
6
|
+
base_url: base_url,
|
|
7
|
+
client_options: client_options.merge({adapter: :typhoeus})
|
|
8
|
+
)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# currently only handles expected cases when calling -renderer
|
|
12
|
+
def parallel_get(requests, client)
|
|
13
|
+
client.in_parallel do
|
|
14
|
+
requests.each do |request|
|
|
15
|
+
request.response = client.get(request.path, request.args) do |req|
|
|
16
|
+
Ecfr.config.request_hook.call(req)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
requests
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.included(base)
|
|
26
|
+
# only include parallel client if the appropriate adapter is present
|
|
27
|
+
|
|
28
|
+
Gem::Specification.find_by_name("faraday-typhoeus")
|
|
29
|
+
base.extend(ClassMethods)
|
|
30
|
+
rescue Gem::LoadError
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module PrinceXmlService
|
|
3
|
+
class Base < Ecfr::Base
|
|
4
|
+
require_relative "pdf"
|
|
5
|
+
|
|
6
|
+
SERVICE_PATH = ""
|
|
7
|
+
|
|
8
|
+
def self.base_url
|
|
9
|
+
Ecfr.config.prince_xml_service_url
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.service_name
|
|
13
|
+
"PrinceXML Service"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module PrinceXmlService
|
|
3
|
+
class Pdf < Base
|
|
4
|
+
class PdfGenerationFailure < StandardError; end
|
|
5
|
+
|
|
6
|
+
PDF_PATH = ""
|
|
7
|
+
|
|
8
|
+
def self.client(client_options = {}, &block)
|
|
9
|
+
client_options[:timeout] = Ecfr.config.prince_xml_service_pdf_timeout
|
|
10
|
+
super(client_options: client_options, &block)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.html_to_pdf(string, output_path, sha)
|
|
14
|
+
response = post(PDF_PATH, {html: string})
|
|
15
|
+
|
|
16
|
+
if response.status != 200 || response.body.blank?
|
|
17
|
+
raise PdfGenerationFailure.new(response.body)
|
|
18
|
+
else
|
|
19
|
+
directory = File.dirname(output_path)
|
|
20
|
+
FileUtils.mkdir_p(directory) unless File.directory?(directory)
|
|
21
|
+
|
|
22
|
+
# re Encoding::UndefinedConversionError ("\xE2" from ASCII-8BIT to UTF-8)
|
|
23
|
+
# faraday force_encoding https://github.com/lostisland/faraday/issues/139
|
|
24
|
+
File.write(output_path, response.body.force_encoding("utf-8"))
|
|
25
|
+
|
|
26
|
+
File.write(File.join(directory, "#{sha}.digest"), output_path)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module RendererService
|
|
3
|
+
class Base < Ecfr::Base
|
|
4
|
+
require_relative "status"
|
|
5
|
+
|
|
6
|
+
require_relative "content"
|
|
7
|
+
require_relative "diff"
|
|
8
|
+
require_relative "origin"
|
|
9
|
+
|
|
10
|
+
SERVICE_PATH = "/api/renderer"
|
|
11
|
+
|
|
12
|
+
def self.base_url
|
|
13
|
+
Ecfr.config.renderer_service_url || Ecfr.config.base_url
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.service_name
|
|
17
|
+
"Renderer Service"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# used by Content and Diff subclasses
|
|
21
|
+
HTML_PATH = "v1/content"
|
|
22
|
+
|
|
23
|
+
# content retrieval doesn't make use of the .perform worflow
|
|
24
|
+
# and so we need to add service path here.
|
|
25
|
+
def self.content_path(date, title_number, view_mode: "enhanced")
|
|
26
|
+
"#{SERVICE_PATH}/#{HTML_PATH}/#{view_mode}/#{date}/title-#{title_number}"
|
|
27
|
+
end
|
|
28
|
+
private_class_method :content_path
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module RendererService
|
|
3
|
+
class Content < Base
|
|
4
|
+
attribute :html,
|
|
5
|
+
desc: "HTML document"
|
|
6
|
+
|
|
7
|
+
def self.find(date, hierarchy, options = {}, client_options: {})
|
|
8
|
+
hierarchy = hierarchy.dup
|
|
9
|
+
|
|
10
|
+
title = hierarchy.delete(:title)
|
|
11
|
+
view_mode = options.delete(:view_mode)
|
|
12
|
+
|
|
13
|
+
complete = hierarchy.delete(:complete)
|
|
14
|
+
options[:skip_ancestry_checks] = 1 if complete
|
|
15
|
+
|
|
16
|
+
new(
|
|
17
|
+
{
|
|
18
|
+
html: get(
|
|
19
|
+
content_path(date, title, view_mode: view_mode),
|
|
20
|
+
hierarchy.merge(options),
|
|
21
|
+
client_options
|
|
22
|
+
).body
|
|
23
|
+
}.stringify_keys
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.url_for(date, title_number, view_mode = "enhanced", params = {})
|
|
28
|
+
path = content_path(date, title_number, view_mode: view_mode)
|
|
29
|
+
|
|
30
|
+
client.build_url(path, params).to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module RendererService
|
|
3
|
+
class Diff < Base
|
|
4
|
+
class Undiffable < StandardError; end
|
|
5
|
+
class DiffTooLarge < Undiffable; end
|
|
6
|
+
|
|
7
|
+
attribute :html,
|
|
8
|
+
desc: "HTML document with inline changes"
|
|
9
|
+
|
|
10
|
+
def self.compare(old_date:, new_date:, hierarchy: {}, options: {})
|
|
11
|
+
hierarchy = hierarchy.dup
|
|
12
|
+
|
|
13
|
+
complete = hierarchy.delete(:complete)
|
|
14
|
+
options[:skip_ancestry_checks] = 1 if complete
|
|
15
|
+
|
|
16
|
+
title = hierarchy.delete(:title)
|
|
17
|
+
view_mode = options.delete(:view_mode) || :enhanced
|
|
18
|
+
params = hierarchy.merge(options).merge(compare: old_date)
|
|
19
|
+
|
|
20
|
+
new(
|
|
21
|
+
{
|
|
22
|
+
html: get(
|
|
23
|
+
content_path(new_date, title, view_mode: view_mode),
|
|
24
|
+
params
|
|
25
|
+
).body
|
|
26
|
+
}.stringify_keys
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module RendererService
|
|
3
|
+
#
|
|
4
|
+
# The Origin API endpoint returns metadata about Source and
|
|
5
|
+
# Authority for the requested hierachy. This metadata is
|
|
6
|
+
# provided for each part in the requested hierarchy as well as
|
|
7
|
+
# any subpart and section/appendix if there are any specific
|
|
8
|
+
# to those levels.
|
|
9
|
+
#
|
|
10
|
+
class Origin < Base
|
|
11
|
+
result_key :origins
|
|
12
|
+
|
|
13
|
+
attribute :identifier,
|
|
14
|
+
desc: "the identifier portion of the level (ex. '121')"
|
|
15
|
+
attribute :label_level,
|
|
16
|
+
desc: "the full identifier and label (ex. 'Part 121')"
|
|
17
|
+
attribute :level,
|
|
18
|
+
desc: "the level being represented (ex. 'part')"
|
|
19
|
+
attribute :link,
|
|
20
|
+
desc: "the path portion of a link to the content for the hierarchy provided"
|
|
21
|
+
attribute :title,
|
|
22
|
+
desc: "the description of the level"
|
|
23
|
+
|
|
24
|
+
attribute :authority,
|
|
25
|
+
type: Array(:string),
|
|
26
|
+
desc: "the Authority content for the level"
|
|
27
|
+
attribute :source,
|
|
28
|
+
type: Array(:string),
|
|
29
|
+
desc: "the Source content for the level"
|
|
30
|
+
|
|
31
|
+
attribute :current,
|
|
32
|
+
type: :boolean,
|
|
33
|
+
desc: "whether this is the hierarchy level requested (instead of an ancestor or descendant)"
|
|
34
|
+
|
|
35
|
+
attribute :hierarchy,
|
|
36
|
+
type: Ecfr::Common::Hierarchy
|
|
37
|
+
|
|
38
|
+
ORIGIN_PATH = "v1/origin"
|
|
39
|
+
|
|
40
|
+
def self.find(date, title_number, params = {})
|
|
41
|
+
supported_params = (Ecfr::Constants::Hierarchy::HIERARCHY_LEVELS[1, 8] + [:build_id]).map(&:to_sym)
|
|
42
|
+
|
|
43
|
+
perform(
|
|
44
|
+
:get,
|
|
45
|
+
origin_path(date, title_number),
|
|
46
|
+
params: params.slice(*supported_params)
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.origin_path(date, title_number)
|
|
51
|
+
"#{ORIGIN_PATH}/#{date}/title-#{title_number}"
|
|
52
|
+
end
|
|
53
|
+
private_class_method :origin_path
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class Base < Ecfr::Base
|
|
4
|
+
require_relative "api_documentation"
|
|
5
|
+
require_relative "status"
|
|
6
|
+
|
|
7
|
+
require_relative "content_version"
|
|
8
|
+
require_relative "facet_base"
|
|
9
|
+
require_relative "date_facet"
|
|
10
|
+
require_relative "title_facet"
|
|
11
|
+
|
|
12
|
+
SERVICE_PATH = "/api/search"
|
|
13
|
+
|
|
14
|
+
def self.base_url
|
|
15
|
+
Ecfr.config.search_service_url || Ecfr.config.base_url
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.service_name
|
|
19
|
+
"Search Service"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class Count < Base
|
|
5
|
+
metadata_key :meta
|
|
6
|
+
|
|
7
|
+
metadata :description,
|
|
8
|
+
desc: "description of the search results"
|
|
9
|
+
|
|
10
|
+
metadata :total_count,
|
|
11
|
+
type: :integer,
|
|
12
|
+
desc: "total count of matching results"
|
|
13
|
+
|
|
14
|
+
COUNT_PATH = "v1/count"
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Retrieves count metadata about a search query
|
|
18
|
+
#
|
|
19
|
+
# @param [<Hash>] args see: {Ecfr::SearchService::ContentVersion::Result.find}
|
|
20
|
+
#
|
|
21
|
+
# @return [<Count>] search query count metadata
|
|
22
|
+
#
|
|
23
|
+
def self.find(args)
|
|
24
|
+
perform(
|
|
25
|
+
:get,
|
|
26
|
+
COUNT_PATH,
|
|
27
|
+
params: args
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class HierarchicalCount
|
|
5
|
+
include AttributeMethodDefinition
|
|
6
|
+
extend Extensible
|
|
7
|
+
|
|
8
|
+
attribute :relation,
|
|
9
|
+
desc: "currently will always return 'eq'"
|
|
10
|
+
|
|
11
|
+
attribute :value,
|
|
12
|
+
type: :integer,
|
|
13
|
+
desc: "total number of matching results"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class HierarchicalCountNode
|
|
5
|
+
include AttributeMethodDefinition
|
|
6
|
+
extend Extensible
|
|
7
|
+
|
|
8
|
+
attribute :heading,
|
|
9
|
+
desc: "the descriptive portion of the CFR level"
|
|
10
|
+
attribute :hierarchy,
|
|
11
|
+
desc: "the shortened identifier portion of the CFR level"
|
|
12
|
+
attribute :hierarchy_heading,
|
|
13
|
+
desc: "the full identifier portion of the CFR level"
|
|
14
|
+
attribute :level,
|
|
15
|
+
desc: "current hierarchy level"
|
|
16
|
+
|
|
17
|
+
attribute :count,
|
|
18
|
+
type: :integer,
|
|
19
|
+
desc: "number of results this hierarchy level contains"
|
|
20
|
+
attribute :max_score,
|
|
21
|
+
type: :integer,
|
|
22
|
+
desc: "the max score of any result this hierarchy level contains"
|
|
23
|
+
|
|
24
|
+
attribute :children,
|
|
25
|
+
type: Array(HierarchicalCountNode),
|
|
26
|
+
desc: "An array of counts data for the next level down in the hierarchy"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class HierarchicalResult < Ecfr::SearchService::ContentVersion
|
|
5
|
+
require_relative "hierarchical_count"
|
|
6
|
+
require_relative "hierarchical_count_node"
|
|
7
|
+
|
|
8
|
+
attribute :count,
|
|
9
|
+
type: HierarchicalCount
|
|
10
|
+
|
|
11
|
+
attribute :max_score,
|
|
12
|
+
type: :float,
|
|
13
|
+
desc: "maximum score for all results"
|
|
14
|
+
|
|
15
|
+
attribute :shown_count,
|
|
16
|
+
type: :integer,
|
|
17
|
+
desc: "number of results returned"
|
|
18
|
+
|
|
19
|
+
attribute :children,
|
|
20
|
+
type: Array(HierarchicalCountNode),
|
|
21
|
+
desc: "recursive array of results nested in their hierarchy"
|
|
22
|
+
|
|
23
|
+
HIERARCHICAL_COUNTS_PATH = "v1/counts/hierarchy"
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# Retrieves results of search query as a hierarchical structure
|
|
27
|
+
#
|
|
28
|
+
# @param [<Hash>] args see: {Ecfr::SearchService::ContentVersion::Result.find}
|
|
29
|
+
#
|
|
30
|
+
# @return [<HierarchicalResult>]
|
|
31
|
+
#
|
|
32
|
+
def self.find(args)
|
|
33
|
+
perform(
|
|
34
|
+
:get,
|
|
35
|
+
HIERARCHICAL_COUNTS_PATH,
|
|
36
|
+
params: args
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class Result < Ecfr::SearchService::ContentVersion
|
|
5
|
+
extend Ecfr::Constants::ChangeTypes
|
|
6
|
+
|
|
7
|
+
result_key :results
|
|
8
|
+
|
|
9
|
+
attribute :full_text_excerpt,
|
|
10
|
+
desc: "excerpt of text where matching keywords were found"
|
|
11
|
+
attribute :type,
|
|
12
|
+
desc: "the content type returned - either *Appendix* or *Section*"
|
|
13
|
+
|
|
14
|
+
attribute :score,
|
|
15
|
+
type: :float,
|
|
16
|
+
desc: "numerical representation of ranking within results"
|
|
17
|
+
|
|
18
|
+
attribute :structure_index,
|
|
19
|
+
type: :integer,
|
|
20
|
+
desc: "index of where content was found in relation to other content within the title - used for sorting"
|
|
21
|
+
|
|
22
|
+
attribute :change_types,
|
|
23
|
+
type: Array(:symbol),
|
|
24
|
+
desc: "array of change types that corresponds to the keys in {KNOWN_CHANGE_TYPES}"
|
|
25
|
+
|
|
26
|
+
attribute :reserved,
|
|
27
|
+
type: :boolean,
|
|
28
|
+
desc: "whether the result represents a portion of the CFR that is Reserved"
|
|
29
|
+
attribute :removed,
|
|
30
|
+
type: :boolean,
|
|
31
|
+
desc: "whether the result represents a part of the CFR that has been removed"
|
|
32
|
+
|
|
33
|
+
attribute :ends_on,
|
|
34
|
+
type: :date,
|
|
35
|
+
desc: "the last date this version of the content appeared in the eCFR"
|
|
36
|
+
attribute :starts_on,
|
|
37
|
+
type: :date,
|
|
38
|
+
desc: "the first date this version of the content appeared in the eCFR"
|
|
39
|
+
|
|
40
|
+
attribute :headings,
|
|
41
|
+
type: Ecfr::Common::Hierarchy,
|
|
42
|
+
desc: "the descriptive portion of the CFR level"
|
|
43
|
+
attribute :hierarchy,
|
|
44
|
+
type: Ecfr::Common::Hierarchy,
|
|
45
|
+
desc: "the shortened identifier portion of the CFR level"
|
|
46
|
+
attribute :hierarchy_headings,
|
|
47
|
+
type: Ecfr::Common::Hierarchy,
|
|
48
|
+
desc: "the full identifier portion of the CFR level"
|
|
49
|
+
|
|
50
|
+
metadata_key :meta
|
|
51
|
+
|
|
52
|
+
metadata :description,
|
|
53
|
+
desc: "description of the search results"
|
|
54
|
+
|
|
55
|
+
metadata :current_page,
|
|
56
|
+
type: :integer,
|
|
57
|
+
desc: "current page of results"
|
|
58
|
+
metadata :total_count,
|
|
59
|
+
type: :integer,
|
|
60
|
+
desc: "total count of matching results across all pages"
|
|
61
|
+
metadata :total_pages,
|
|
62
|
+
type: :integer,
|
|
63
|
+
desc: "total number of pages in the search results"
|
|
64
|
+
|
|
65
|
+
metadata :max_score,
|
|
66
|
+
type: :float,
|
|
67
|
+
desc: "the maximum score of any match in the search"
|
|
68
|
+
|
|
69
|
+
# returned when pagingate_by=date option is provided
|
|
70
|
+
# as part of the search query
|
|
71
|
+
metadata :min_date,
|
|
72
|
+
type: :date,
|
|
73
|
+
desc: "the minimum date of all returned results"
|
|
74
|
+
# returned when pagingate_by=date option is provided
|
|
75
|
+
# as part of the search query
|
|
76
|
+
metadata :max_date,
|
|
77
|
+
type: :date,
|
|
78
|
+
desc: "the maximum date of all returned results"
|
|
79
|
+
|
|
80
|
+
RESULTS_PATH = "v1/results"
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# Retrieve results of search query
|
|
84
|
+
#
|
|
85
|
+
# @param [<Hash>] args supported search parameters; supported params:
|
|
86
|
+
# @option args [String] :date ISO date or 'current'
|
|
87
|
+
# @option args [String] :last_modified_after ISO date
|
|
88
|
+
# @option args [String] :last_modified_before ISO date
|
|
89
|
+
# @option args [String] :last_modified_on_or_after ISO date
|
|
90
|
+
# @option args [String] :last_modified_on_or_before ISO date
|
|
91
|
+
# @option args [String] :order relevance, hierarchy, newest_first, oldest_first
|
|
92
|
+
# @option args [Integer] :page page of search results to return
|
|
93
|
+
# @option args [Integer] :per_page number of results per page
|
|
94
|
+
# @option args [String] :paginate_by one of "results" or "date"
|
|
95
|
+
# if "date" then one of the last_modified_* options is required
|
|
96
|
+
# @option args [String] :query search terms
|
|
97
|
+
#
|
|
98
|
+
# @return [[<Result>]] an array of results with metadata
|
|
99
|
+
#
|
|
100
|
+
def self.find(args)
|
|
101
|
+
perform(
|
|
102
|
+
:get,
|
|
103
|
+
RESULTS_PATH,
|
|
104
|
+
params: args.compact
|
|
105
|
+
)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class Suggestion < Ecfr::SearchService::ContentVersion
|
|
5
|
+
result_key :suggestions
|
|
6
|
+
|
|
7
|
+
attribute :highlight,
|
|
8
|
+
desc: "text containing '<mark>' tags the denote where the query was
|
|
9
|
+
found by the search engine - only type: 'autocomplete'"
|
|
10
|
+
# attribute :summary,
|
|
11
|
+
# desc: "description of the search - only type: 'cfr_reference'"
|
|
12
|
+
attribute :type,
|
|
13
|
+
desc: "the type of suggestion, currently 'cfr_reference' or 'autocomplete'"
|
|
14
|
+
|
|
15
|
+
attribute :reserved,
|
|
16
|
+
type: :boolean,
|
|
17
|
+
desc: "whether the result represents a portion of the CFR that is Reserved"
|
|
18
|
+
|
|
19
|
+
attribute :removed,
|
|
20
|
+
type: :date,
|
|
21
|
+
desc: "the date that this portion of the CFR that was removed"
|
|
22
|
+
|
|
23
|
+
attribute :structure_index,
|
|
24
|
+
type: :integer,
|
|
25
|
+
desc: "an integer representing the location of the suggestion in the
|
|
26
|
+
source xml for it's corresponding title. Useful for sorting. Only
|
|
27
|
+
type: 'autocomplete'"
|
|
28
|
+
|
|
29
|
+
attribute :headings,
|
|
30
|
+
type: Ecfr::Common::Hierarchy,
|
|
31
|
+
desc: "the descriptive portion of the CFR level"
|
|
32
|
+
attribute :hierarchy,
|
|
33
|
+
type: Ecfr::Common::Hierarchy,
|
|
34
|
+
desc: "the shortened identifier portion of the CFR level"
|
|
35
|
+
attribute :hierarchy_headings,
|
|
36
|
+
type: Ecfr::Common::Hierarchy,
|
|
37
|
+
desc: "the full identifier portion of the CFR level"
|
|
38
|
+
|
|
39
|
+
metadata_key :meta
|
|
40
|
+
|
|
41
|
+
metadata :global_search_results,
|
|
42
|
+
type: :integer,
|
|
43
|
+
desc: "total number of matching full text search results"
|
|
44
|
+
metadata :narrowed_search_results,
|
|
45
|
+
type: :integer,
|
|
46
|
+
desc: "number of search results that match the current hierarchy"
|
|
47
|
+
|
|
48
|
+
metadata :narrowed_hierarchy,
|
|
49
|
+
type: Ecfr::Common::Hierarchy,
|
|
50
|
+
desc: "the hierarchy that the narrowed_search_results have been limited to"
|
|
51
|
+
|
|
52
|
+
SUGGESTIONS_PATH = "v1/suggestions"
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
# Retreive suggestions for the given search args
|
|
56
|
+
#
|
|
57
|
+
# @param [<Hash>] args see: {Ecfr::SearchService::ContentVersion::Result.find}
|
|
58
|
+
# @option Boolean autocomplete Adding autocomplete=true to the request
|
|
59
|
+
# will return suggestions of type 'autocomplete'
|
|
60
|
+
#
|
|
61
|
+
# @return [<Suggestion>] by default suggestions will be of type
|
|
62
|
+
# 'cfr_reference' and are useful for refining full text search requests.
|
|
63
|
+
# When autocomplete=true has been added to the request then suggestions
|
|
64
|
+
# will be of type 'autocomplete' and are useful for typeahead style searches
|
|
65
|
+
#
|
|
66
|
+
def self.find(args)
|
|
67
|
+
perform(
|
|
68
|
+
:get,
|
|
69
|
+
SUGGESTIONS_PATH,
|
|
70
|
+
params: args.except(:order, :page, :per_page).stringify_keys
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Ecfr
|
|
2
|
+
module SearchService
|
|
3
|
+
class ContentVersion
|
|
4
|
+
class Summary < Ecfr::SearchService::ContentVersion
|
|
5
|
+
metadata_key :meta
|
|
6
|
+
|
|
7
|
+
metadata :description
|
|
8
|
+
|
|
9
|
+
SUMMARY_PATH = "v1/summary"
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# <Description>
|
|
13
|
+
#
|
|
14
|
+
# @param [<Hash>] args search arguments - everthing except
|
|
15
|
+
# :hierarchy and :query will be removed
|
|
16
|
+
#
|
|
17
|
+
# @return [<Summary>]
|
|
18
|
+
#
|
|
19
|
+
def self.find(args)
|
|
20
|
+
valid_args = args.slice(:hierarchy, :query)
|
|
21
|
+
|
|
22
|
+
perform(:get, SUMMARY_PATH, params: valid_args)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|