ecfr 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +11 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +3 -0
  5. data/.rspec_parallel +4 -0
  6. data/.rubocop.yml +28 -0
  7. data/.yardopts +3 -0
  8. data/CHANGELOG.md +6 -0
  9. data/Dockerfile +6 -0
  10. data/Gemfile +6 -0
  11. data/Gemfile.lock +138 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +133 -0
  14. data/Rakefile +12 -0
  15. data/bin/console +15 -0
  16. data/bin/setup +8 -0
  17. data/ecfr.gemspec +74 -0
  18. data/lib/ecfr/admin_service/agency/hierarchy.rb +27 -0
  19. data/lib/ecfr/admin_service/agency.rb +34 -0
  20. data/lib/ecfr/admin_service/api_documentation.rb +7 -0
  21. data/lib/ecfr/admin_service/base.rb +26 -0
  22. data/lib/ecfr/admin_service/build.rb +38 -0
  23. data/lib/ecfr/admin_service/ecfr_correction/cfr_reference.rb +17 -0
  24. data/lib/ecfr/admin_service/ecfr_correction.rb +78 -0
  25. data/lib/ecfr/admin_service/editorial_note/hierarchy.rb +19 -0
  26. data/lib/ecfr/admin_service/editorial_note.rb +40 -0
  27. data/lib/ecfr/admin_service/ibr_cfr_range/address.rb +17 -0
  28. data/lib/ecfr/admin_service/ibr_cfr_range/organization.rb +28 -0
  29. data/lib/ecfr/admin_service/ibr_cfr_range.rb +67 -0
  30. data/lib/ecfr/admin_service/issue/change.rb +19 -0
  31. data/lib/ecfr/admin_service/issue.rb +86 -0
  32. data/lib/ecfr/admin_service/site_notification.rb +34 -0
  33. data/lib/ecfr/admin_service/status.rb +7 -0
  34. data/lib/ecfr/attribute_caster.rb +72 -0
  35. data/lib/ecfr/attribute_method_definition.rb +92 -0
  36. data/lib/ecfr/base.rb +71 -0
  37. data/lib/ecfr/client.rb +318 -0
  38. data/lib/ecfr/common/hierarchy.rb +35 -0
  39. data/lib/ecfr/configuration.rb +58 -0
  40. data/lib/ecfr/constants.rb +21 -0
  41. data/lib/ecfr/default_documentation_setup.rb +39 -0
  42. data/lib/ecfr/default_status_setup.rb +46 -0
  43. data/lib/ecfr/diff_service/base.rb +17 -0
  44. data/lib/ecfr/diff_service/status.rb +34 -0
  45. data/lib/ecfr/extensible.rb +45 -0
  46. data/lib/ecfr/facet_attribute_method_definition.rb +47 -0
  47. data/lib/ecfr/faraday/user_agent/middleware.rb +14 -0
  48. data/lib/ecfr/ofr_profile_service/base.rb +20 -0
  49. data/lib/ecfr/ofr_profile_service/status.rb +7 -0
  50. data/lib/ecfr/parallel_client.rb +33 -0
  51. data/lib/ecfr/prince_xml_service/base.rb +17 -0
  52. data/lib/ecfr/prince_xml_service/pdf.rb +31 -0
  53. data/lib/ecfr/renderer_service/base.rb +31 -0
  54. data/lib/ecfr/renderer_service/content.rb +34 -0
  55. data/lib/ecfr/renderer_service/diff.rb +31 -0
  56. data/lib/ecfr/renderer_service/origin.rb +56 -0
  57. data/lib/ecfr/renderer_service/status.rb +7 -0
  58. data/lib/ecfr/request_representation.rb +12 -0
  59. data/lib/ecfr/search_service/api_documentation.rb +7 -0
  60. data/lib/ecfr/search_service/base.rb +23 -0
  61. data/lib/ecfr/search_service/content_version/count.rb +33 -0
  62. data/lib/ecfr/search_service/content_version/hierarchical_count.rb +17 -0
  63. data/lib/ecfr/search_service/content_version/hierarchical_count_node.rb +30 -0
  64. data/lib/ecfr/search_service/content_version/hierarchichal_result.rb +42 -0
  65. data/lib/ecfr/search_service/content_version/result.rb +110 -0
  66. data/lib/ecfr/search_service/content_version/suggestion.rb +76 -0
  67. data/lib/ecfr/search_service/content_version/summary.rb +27 -0
  68. data/lib/ecfr/search_service/content_version.rb +85 -0
  69. data/lib/ecfr/search_service/date_facet.rb +19 -0
  70. data/lib/ecfr/search_service/facet_base.rb +55 -0
  71. data/lib/ecfr/search_service/status.rb +7 -0
  72. data/lib/ecfr/search_service/title_facet.rb +18 -0
  73. data/lib/ecfr/subscriptions_service/base.rb +19 -0
  74. data/lib/ecfr/subscriptions_service/status.rb +7 -0
  75. data/lib/ecfr/subscriptions_service/subscription.rb +97 -0
  76. data/lib/ecfr/testing/extensions/admin_service/ecfr_correction_extensions.rb +13 -0
  77. data/lib/ecfr/testing/extensions/admin_service/issue_extensions.rb +13 -0
  78. data/lib/ecfr/testing/extensions/renderer_service/origin_extensions.rb +13 -0
  79. data/lib/ecfr/testing/extensions/search_service/content_version_result_extensions.rb +16 -0
  80. data/lib/ecfr/testing/extensions/search_service/date_facet_extensions.rb +13 -0
  81. data/lib/ecfr/testing/extensions/versioner_service/ancestors_extensions.rb +20 -0
  82. data/lib/ecfr/testing/extensions/versioner_service/title_extenstions.rb +16 -0
  83. data/lib/ecfr/testing/factories/admin_service/cfr_reference_factory.rb +14 -0
  84. data/lib/ecfr/testing/factories/admin_service/ecfr_correction_factory.rb +31 -0
  85. data/lib/ecfr/testing/factories/admin_service/issue_change_factory.rb +12 -0
  86. data/lib/ecfr/testing/factories/admin_service/issue_factory.rb +21 -0
  87. data/lib/ecfr/testing/factories/common/hierarchy_factory.rb +36 -0
  88. data/lib/ecfr/testing/factories/renderer_service/origin_factory.rb +32 -0
  89. data/lib/ecfr/testing/factories/search_service/content_version_count_factory.rb +20 -0
  90. data/lib/ecfr/testing/factories/search_service/content_version_result_factory.rb +76 -0
  91. data/lib/ecfr/testing/factories/search_service/date_facet_factory.rb +12 -0
  92. data/lib/ecfr/testing/factories/versioner_service/ancestors_factory.rb +26 -0
  93. data/lib/ecfr/testing/factories/versioner_service/metadata_node_info_factory.rb +15 -0
  94. data/lib/ecfr/testing/factories/versioner_service/node_summary_factory.rb +16 -0
  95. data/lib/ecfr/testing/factories/versioner_service/structure_factory.rb +57 -0
  96. data/lib/ecfr/testing/factories/versioner_service/title_factory.rb +36 -0
  97. data/lib/ecfr/testing/factory_bot_helpers/content_version.rb +38 -0
  98. data/lib/ecfr/testing/factory_bot_helpers/ecfr_gem_initialize_helpers.rb +51 -0
  99. data/lib/ecfr/testing/helpers/response_helper.rb +5 -0
  100. data/lib/ecfr/testing/strategies/ecfr_attribute_hash_strategy.rb +37 -0
  101. data/lib/ecfr/testing.rb +28 -0
  102. data/lib/ecfr/version.rb +5 -0
  103. data/lib/ecfr/versioner_service/ancestors/metadata_node_info.rb +22 -0
  104. data/lib/ecfr/versioner_service/ancestors/node_summary.rb +54 -0
  105. data/lib/ecfr/versioner_service/ancestors.rb +152 -0
  106. data/lib/ecfr/versioner_service/api_documentation.rb +7 -0
  107. data/lib/ecfr/versioner_service/base.rb +24 -0
  108. data/lib/ecfr/versioner_service/status.rb +7 -0
  109. data/lib/ecfr/versioner_service/structure.rb +120 -0
  110. data/lib/ecfr/versioner_service/title.rb +78 -0
  111. data/lib/ecfr/versioner_service/xml_content.rb +59 -0
  112. data/lib/ecfr.rb +90 -0
  113. data/lib/yard/attribute_handler.rb +87 -0
  114. data/lib/yard/metadata_handler.rb +87 -0
  115. metadata +389 -0
@@ -0,0 +1,7 @@
1
+ module Ecfr
2
+ module OfrProfileService
3
+ class Status < Base
4
+ include DefaultStatusSetup
5
+ end
6
+ end
7
+ end
@@ -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,7 @@
1
+ module Ecfr
2
+ module RendererService
3
+ class Status < Base
4
+ include DefaultStatusSetup
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Ecfr
2
+ class RequestRepresentation
3
+ attr_reader :args, :options, :path
4
+ attr_accessor :response
5
+
6
+ def initialize(path:, args:, options: {})
7
+ @args = args
8
+ @options = options
9
+ @path = path
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module Ecfr
2
+ module SearchService
3
+ class ApiDocumentation < Base
4
+ include DefaultDocumentationSetup
5
+ end
6
+ end
7
+ 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