ecfr 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|