publishing_platform_api_adapters 0.7.1 → 0.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc390ba19e31be9e7e3457b2f4f3534e3da0023a41854b22b0db5fa11e364588
4
- data.tar.gz: 06c5faed53a2faf13ee59a5f0a304343204c890ece1ddfbdae69da7a8135bfe5
3
+ metadata.gz: 12a639bdc05f05fbcb78557b20924d4d95286b09af685f76f5e2b6989d998bd7
4
+ data.tar.gz: '0674539db2157aac06598a56fe898cbcb1ce4baa847f2bef637d62dbe4947501'
5
5
  SHA512:
6
- metadata.gz: 929d2167c3e4f3342f21f25923218ca718cdeb00679ac58c652a69baf69b6dbee815266191946f3fad3b52e94b2323de02d7b33d2a71de915d8b9b35a6eb2138
7
- data.tar.gz: e982309742921c2ecbafba1d6a7844d8e4f0371ea77ac2ec99cbf31dc202029013db71b0b454357ecfcb07dc4b031fe100fb3420f2d36d9dc39221ffa15b1183
6
+ metadata.gz: 19667cbc60807f1d7c3fe54eb1ab0a8bd5437fe328f011cd5fc8e1bfa13085218c84bd80a146c645a18cc65af8ddef2dceec9b14a46f689508be7abe737b16c3
7
+ data.tar.gz: e77e8a1bd9b420c84dfdbf09bcbee8d672eab10f27422a14d6a0568572f0f8286e4a8b11cc25969a70d3e95c67bcde71c46cc55851bdb6c57070c47550e8c6f7
@@ -16,9 +16,96 @@ class PublishingPlatformApi::ContentStore < PublishingPlatformApi::Base
16
16
  raise ItemNotFound.build_from(e)
17
17
  end
18
18
 
19
+ # Returns an array tuple of destination url with status code e.g
20
+ # ["https://www.publishing-platform.co.uk/destination", 301]
21
+ def self.redirect_for_path(content_item, request_path, request_query = "")
22
+ RedirectResolver.call(content_item, request_path, request_query)
23
+ end
24
+
19
25
  private
20
26
 
21
27
  def content_item_url(base_path)
22
28
  "#{endpoint}/content#{base_path}"
23
29
  end
30
+
31
+ class RedirectResolver
32
+ def initialize(content_item, request_path, request_query = "")
33
+ @content_item = content_item
34
+ @request_path = request_path
35
+ @request_query = request_query.to_s
36
+ end
37
+
38
+ def self.call(*args)
39
+ new(*args).call
40
+ end
41
+
42
+ def call
43
+ redirect = redirect_for_path(request_path)
44
+ raise UnresolvedRedirect, "Could not find a matching redirect" unless redirect
45
+
46
+ destination_uri = URI.parse(
47
+ resolve_destination(redirect, request_path, request_query),
48
+ )
49
+
50
+ url = if destination_uri.absolute?
51
+ destination_uri.to_s
52
+ else
53
+ "#{PublishingPlatformLocation.new.website_root}#{destination_uri}"
54
+ end
55
+
56
+ [url, 301]
57
+ end
58
+
59
+ private_class_method :new
60
+
61
+ private
62
+
63
+ attr_reader :content_item, :request_path, :request_query
64
+
65
+ def redirect_for_path(path)
66
+ redirects_by_segments.find do |r|
67
+ next true if r["path"] == path
68
+
69
+ route_prefix_match?(r["path"], path) if r["type"] == "prefix"
70
+ end
71
+ end
72
+
73
+ def redirects_by_segments
74
+ redirects = content_item["redirects"] || []
75
+ redirects.sort_by { |r| r["path"].split("/").count * -1 }
76
+ end
77
+
78
+ def route_prefix_match?(prefix_path, path_to_match)
79
+ prefix_regex = %r{^#{Regexp.escape(prefix_path)}/}
80
+ path_to_match.match prefix_regex
81
+ end
82
+
83
+ def resolve_destination(redirect, path, query)
84
+ return redirect["destination"] unless redirect["segments_mode"] == "preserve"
85
+
86
+ if redirect["type"] == "prefix"
87
+ prefix_destination(redirect, path, query)
88
+ else
89
+ redirect["destination"] + (query.empty? ? "" : "?#{query}")
90
+ end
91
+ end
92
+
93
+ def prefix_destination(redirect, path, query)
94
+ uri = URI.parse(redirect["destination"])
95
+ start_char = redirect["path"].length
96
+ suffix = path[start_char..]
97
+
98
+ if uri.path == "" && suffix[0] != "/"
99
+ uri.path = "/#{suffix}"
100
+ else
101
+ uri.path += suffix
102
+ end
103
+
104
+ uri.query = query if uri.query.nil? && !query.empty?
105
+
106
+ uri.to_s
107
+ end
108
+ end
109
+
110
+ class UnresolvedRedirect < PublishingPlatformApi::BaseError; end
24
111
  end
@@ -0,0 +1,41 @@
1
+ module PublishingPlatformApi
2
+ module TestHelpers
3
+ module ContentItemHelpers
4
+ def content_item_for_base_path(base_path)
5
+ {
6
+ "title" => titleize_base_path(base_path),
7
+ "description" => "Description for #{base_path}",
8
+ "schema_name" => "answer",
9
+ "document_type" => "answer",
10
+ "public_updated_at" => "2014-05-06T12:01:00+00:00",
11
+ # base_path is added in as necessary (ie for content-store GET responses)
12
+ # "base_path" => base_path,
13
+ "details" => {
14
+ "body" => "Some content for #{base_path}",
15
+ },
16
+ }
17
+ end
18
+
19
+ def gone_content_item_for_base_path(base_path)
20
+ {
21
+ "title" => nil,
22
+ "description" => nil,
23
+ "document_type" => "gone",
24
+ "schema_name" => "gone",
25
+ "public_updated_at" => nil,
26
+ "base_path" => base_path,
27
+ "withdrawn_notice" => {},
28
+ "details" => {},
29
+ }
30
+ end
31
+
32
+ def titleize_base_path(base_path, options = {})
33
+ if options[:title_case]
34
+ base_path.tr("-", " ").gsub(/\b./, &:upcase)
35
+ else
36
+ base_path.gsub(%r{[-/]}, " ").strip.capitalize
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,84 @@
1
+ require "publishing_platform_api/test_helpers/content_item_helpers"
2
+ require "json"
3
+
4
+ module PublishingPlatformApi
5
+ module TestHelpers
6
+ module ContentStore
7
+ include ContentItemHelpers
8
+
9
+ def content_store_endpoint(draft: false)
10
+ draft ? PublishingPlatformLocation.find("draft-content-store") : PublishingPlatformLocation.find("content-store")
11
+ end
12
+
13
+ # Stubs a content item in the content store.
14
+ # The following options can be passed in:
15
+ #
16
+ # :max_age will set the max-age of the Cache-Control header in the response. Defaults to 900
17
+ # :private if true, the Cache-Control header will include the "private" directive. By default it
18
+ # will include "public"
19
+ # :draft will point to the draft content store if set to true
20
+ def stub_content_store_has_item(base_path, body = content_item_for_base_path(base_path), options = {})
21
+ max_age = options.fetch(:max_age, 900)
22
+ visibility = options[:private] ? "private" : "public"
23
+ body = body.to_json unless body.is_a?(String)
24
+
25
+ endpoint = content_store_endpoint(draft: options[:draft])
26
+ stub_request(:get, "#{endpoint}/content#{base_path}").to_return(
27
+ status: 200,
28
+ body:,
29
+ headers: {
30
+ cache_control: "#{visibility}, max-age=#{max_age}",
31
+ date: Time.now.httpdate,
32
+ },
33
+ )
34
+ end
35
+
36
+ def stub_content_store_does_not_have_item(base_path, options = {})
37
+ endpoint = content_store_endpoint(draft: options[:draft])
38
+ stub_request(:get, "#{endpoint}/content#{base_path}").to_return(status: 404, headers: {})
39
+ end
40
+
41
+ # Content store has gone item
42
+ #
43
+ # Stubs a content item in the content store to respond with 410 HTTP Status Code and response body with 'format' set to 'gone'.
44
+ #
45
+ # @param base_path [String]
46
+ # @param body [Hash]
47
+ # @param options [Hash]
48
+ # @option options [String] draft Will point to the draft content store if set to true
49
+ #
50
+ # @example
51
+ #
52
+ # stub_content_store.stub_content_store_has_gone_item('/sample-slug')
53
+ #
54
+ # # Will return HTTP Status Code 410 and the following response body:
55
+ # {
56
+ # "title" => nil,
57
+ # "description" => nil,
58
+ # "format" => "gone",
59
+ # "schema_name" => "gone",
60
+ # "public_updated_at" => nil,
61
+ # "base_path" => "/sample-slug",
62
+ # "withdrawn_notice" => {},
63
+ # "details" => {}
64
+ # }
65
+ def stub_content_store_has_gone_item(base_path, body = gone_content_item_for_base_path(base_path), options = {})
66
+ body = body.to_json unless body.is_a?(String)
67
+ endpoint = content_store_endpoint(draft: options[:draft])
68
+ stub_request(:get, "#{endpoint}/content#{base_path}").to_return(
69
+ status: 410,
70
+ body:,
71
+ headers: {},
72
+ )
73
+ end
74
+
75
+ def stub_content_store_isnt_available
76
+ stub_request(:any, /#{content_store_endpoint}\/.*/).to_return(status: 503)
77
+ end
78
+
79
+ def content_item_for_base_path(base_path)
80
+ super.merge("base_path" => base_path)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PublishingPlatformApi
4
- VERSION = "0.7.1"
4
+ VERSION = "0.8.1"
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: publishing_platform_api_adapters
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Publishing Platform
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: addressable
@@ -114,6 +114,8 @@ files:
114
114
  - lib/publishing_platform_api/railtie.rb
115
115
  - lib/publishing_platform_api/response.rb
116
116
  - lib/publishing_platform_api/router.rb
117
+ - lib/publishing_platform_api/test_helpers/content_item_helpers.rb
118
+ - lib/publishing_platform_api/test_helpers/content_store.rb
117
119
  - lib/publishing_platform_api/version.rb
118
120
  - lib/publishing_platform_api_adapters.rb
119
121
  licenses:
@@ -133,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
135
  - !ruby/object:Gem::Version
134
136
  version: '0'
135
137
  requirements: []
136
- rubygems_version: 3.6.3
138
+ rubygems_version: 3.6.7
137
139
  specification_version: 4
138
140
  summary: Adapters to work with Publishing Platform APIs
139
141
  test_files: []