govuk_schemas 3.2.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +17 -0
- data/.rubocop.yml +5 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +27 -0
- data/Gemfile +1 -1
- data/Jenkinsfile +3 -45
- data/govuk_schemas.gemspec +7 -8
- data/lib/govuk_schemas.rb +1 -2
- data/lib/govuk_schemas/random_content_generator.rb +128 -0
- data/lib/govuk_schemas/random_example.rb +9 -4
- data/lib/govuk_schemas/{random_item_generator.rb → random_schema_generator.rb} +49 -34
- data/lib/govuk_schemas/rspec_matchers.rb +18 -27
- data/lib/govuk_schemas/schema.rb +2 -3
- data/lib/govuk_schemas/version.rb +1 -1
- metadata +23 -24
- data/lib/govuk_schemas/random.rb +0 -101
- data/lib/govuk_schemas/utils.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8a44876dfefc37456d8e08d8ba63a9bd95c07425f12d44ca51aafdbbd01e513c
|
4
|
+
data.tar.gz: '0928c1cae2cf6eb2cf31adfb2eb2d4da30942178ac2e5b9bc2e3681aef3bce3b'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14265d1073c7a9b8efe6225d2f7e5508ea91fa4f25e04cb377afe09e9cd3423d98c3fcc319888773301e0f2e7e940b911c159cf83ee184f793f98d756d3909e3
|
7
|
+
data.tar.gz: b1dd19d78453ac71cae823c987c558f18bcca9f6295908cfb96e6b084a7a85d0543ff72da724fd2c8fa6cc700adbce39b200fcca14ad113333b00a8260b3cc77
|
@@ -0,0 +1,17 @@
|
|
1
|
+
version: 2
|
2
|
+
updates:
|
3
|
+
- package-ecosystem: bundler
|
4
|
+
directory: /
|
5
|
+
schedule:
|
6
|
+
interval: daily
|
7
|
+
allow:
|
8
|
+
# Internal gems
|
9
|
+
- dependency-name: "govuk*"
|
10
|
+
dependency-type: direct
|
11
|
+
- dependency-name: rubocop-govuk
|
12
|
+
dependency-type: direct
|
13
|
+
# Framework gems
|
14
|
+
- dependency-name: rake
|
15
|
+
dependency-type: direct
|
16
|
+
- dependency-name: rspec
|
17
|
+
dependency-type: direct
|
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,30 @@
|
|
1
|
+
# 4.2.0
|
2
|
+
|
3
|
+
* Add support for generating random HH:MM time strings that match a regex. ([#62](https://github.com/alphagov/govuk_schemas/pull/62))
|
4
|
+
|
5
|
+
# 4.1.1
|
6
|
+
|
7
|
+
* Fix RandomSchemaGenerator.new always returning equivalent generators ([#60](https://github.com/alphagov/govuk_schemas/pull/60))
|
8
|
+
|
9
|
+
# 4.1.0
|
10
|
+
|
11
|
+
* Add `seed` parameter to `GovukSchemas::RandomExample` to make the random behaviour deterministic. Given the same seed, the same randomised outputs will be returned ([#56](https://github.com/alphagov/govuk_schemas/pull/56)).
|
12
|
+
|
13
|
+
# 4.0.1
|
14
|
+
|
15
|
+
* Bump the required Ruby version to >= 2.6.x.
|
16
|
+
|
17
|
+
# 4.0.0
|
18
|
+
|
19
|
+
* Change RSpec::Matchers, rename `be_valid_against_schema` to
|
20
|
+
`be_valid_against_publisher_schema` and add
|
21
|
+
`be_valid_against_frontend_schema` plus
|
22
|
+
`be_valid_against_notification_schema`.
|
23
|
+
|
24
|
+
# 3.3.0
|
25
|
+
|
26
|
+
* Support generating objects with an `oneOf` property.
|
27
|
+
|
1
28
|
# 3.2.0
|
2
29
|
|
3
30
|
* Add `GovukSchemas::DocumentTypes.valid_document_types` (PR #48)
|
data/Gemfile
CHANGED
data/Jenkinsfile
CHANGED
@@ -2,50 +2,8 @@
|
|
2
2
|
|
3
3
|
library("govuk")
|
4
4
|
|
5
|
-
REPOSITORY = 'govuk_schemas'
|
6
|
-
|
7
5
|
node {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
checkout scm
|
12
|
-
}
|
13
|
-
|
14
|
-
stage('Clean') {
|
15
|
-
govuk.cleanupGit()
|
16
|
-
govuk.mergeMasterBranch()
|
17
|
-
}
|
18
|
-
|
19
|
-
stage("Set up content schema dependency") {
|
20
|
-
govuk.contentSchemaDependency()
|
21
|
-
}
|
22
|
-
|
23
|
-
stage('Bundle') {
|
24
|
-
echo 'Bundling'
|
25
|
-
sh("bundle install --path ${JENKINS_HOME}/bundles/${JOB_NAME}")
|
26
|
-
}
|
27
|
-
|
28
|
-
stage('Linter') {
|
29
|
-
govuk.rubyLinter()
|
30
|
-
}
|
31
|
-
|
32
|
-
stage('Tests') {
|
33
|
-
govuk.setEnvar('RAILS_ENV', 'test')
|
34
|
-
govuk.runTests('spec')
|
35
|
-
}
|
36
|
-
|
37
|
-
if(env.BRANCH_NAME == "master") {
|
38
|
-
stage('Publish Gem') {
|
39
|
-
govuk.publishGem(REPOSITORY, env.BRANCH_NAME)
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
} catch (e) {
|
44
|
-
currentBuild.result = 'FAILED'
|
45
|
-
step([$class: 'Mailer',
|
46
|
-
notifyEveryUnstableBuild: true,
|
47
|
-
recipients: 'govuk-ci-notifications@digital.cabinet-office.gov.uk',
|
48
|
-
sendToIndividuals: true])
|
49
|
-
throw e
|
50
|
-
}
|
6
|
+
govuk.buildProject(
|
7
|
+
rubyLintDiff: false,
|
8
|
+
)
|
51
9
|
}
|
data/govuk_schemas.gemspec
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
3
|
+
require "govuk_schemas/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
6
|
spec.name = "govuk_schemas"
|
@@ -17,16 +16,16 @@ Gem::Specification.new do |spec|
|
|
17
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
17
|
spec.bindir = "exe"
|
19
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = %w[lib]
|
21
20
|
|
22
21
|
# This should be kept in sync with the json-schema version of govuk-content-schemas.
|
23
22
|
spec.add_dependency "json-schema", "~> 2.8.0"
|
24
23
|
|
25
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
-
spec.add_development_dependency "rspec", "~> 3.4"
|
27
24
|
spec.add_development_dependency "pry-byebug"
|
28
|
-
spec.add_development_dependency "
|
25
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
27
|
+
spec.add_development_dependency "rubocop-govuk", "~> 3.8"
|
29
28
|
spec.add_development_dependency "yard", "~> 0.8"
|
30
29
|
|
31
|
-
spec.required_ruby_version = ">= 2.
|
30
|
+
spec.required_ruby_version = ">= 2.6"
|
32
31
|
end
|
data/lib/govuk_schemas.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "govuk_schemas/version"
|
2
2
|
require "govuk_schemas/schema"
|
3
|
-
require "govuk_schemas/utils"
|
4
3
|
require "govuk_schemas/random_example"
|
5
4
|
require "govuk_schemas/document_types"
|
6
5
|
require "govuk_schemas/example"
|
@@ -10,6 +9,6 @@ module GovukSchemas
|
|
10
9
|
CONTENT_SCHEMA_DIR = ENV["GOVUK_CONTENT_SCHEMAS_PATH"] || "../govuk-content-schemas"
|
11
10
|
|
12
11
|
# @private
|
13
|
-
class InvalidContentGenerated <
|
12
|
+
class InvalidContentGenerated < RuntimeError
|
14
13
|
end
|
15
14
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module GovukSchemas
|
2
|
+
# @private
|
3
|
+
class RandomContentGenerator
|
4
|
+
WORDS = %w[Lorem ipsum dolor sit amet consectetur adipiscing elit. Ut suscipit at mauris non bibendum. Ut ac massa est. Aenean tempor imperdiet leo vel interdum. Nam sagittis cursus sem ultricies scelerisque. Quisque porttitor risus vel risus finibus eu sollicitudin nisl aliquet. Sed sed lectus ac dolor molestie interdum. Nam molestie pellentesque purus ac vestibulum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse non tempor eros. Mauris eu orci hendrerit volutpat lorem in tristique libero. Duis a nibh nibh.].freeze
|
5
|
+
|
6
|
+
def initialize(random: Random.new)
|
7
|
+
@random = random
|
8
|
+
end
|
9
|
+
|
10
|
+
def string_for_type(type)
|
11
|
+
if type == "date-time"
|
12
|
+
time
|
13
|
+
elsif type == "uri"
|
14
|
+
uri
|
15
|
+
else
|
16
|
+
raise "Unknown attribute type `#{type}`"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def time
|
21
|
+
arbitrary_time = Time.new(2012, 2, 1)
|
22
|
+
(arbitrary_time + @random.rand(0..500_000_000)).iso8601
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO: make this more random with query string, optional anchor.
|
26
|
+
def uri
|
27
|
+
"http://example.com#{base_path}#{anchor}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def base_path
|
31
|
+
"/" + @random.rand(1..5).times.map { uuid }.join("/")
|
32
|
+
end
|
33
|
+
|
34
|
+
def govuk_subdomain_url
|
35
|
+
subdomain = @random.rand(2..4).times.map {
|
36
|
+
("a".."z").to_a.sample(@random.rand(3..8), random: @random).join
|
37
|
+
}.join(".")
|
38
|
+
"https://#{subdomain}.gov.uk#{base_path}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def string(minimum_chars = nil, maximum_chars = nil)
|
42
|
+
minimum_chars ||= 0
|
43
|
+
maximum_chars ||= 100
|
44
|
+
WORDS.sample(@random.rand(minimum_chars..maximum_chars), random: @random).join(" ")
|
45
|
+
end
|
46
|
+
|
47
|
+
def bool
|
48
|
+
@random.rand(2) == 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def anchor
|
52
|
+
"##{hex}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def random_identifier(separator:)
|
56
|
+
WORDS.sample(@random.rand(1..10), random: @random)
|
57
|
+
.join("-")
|
58
|
+
.gsub(/[^a-z0-9\-_]+/i, "-")
|
59
|
+
.gsub("-", separator)
|
60
|
+
end
|
61
|
+
|
62
|
+
def uuid
|
63
|
+
# matches uuid regex e.g. e058aad7-ce86-5181-8801-4ddcb3c8f27c
|
64
|
+
# /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/
|
65
|
+
"#{hex(8)}-#{hex(4)}-1#{hex(3)}-a#{hex(3)}-#{hex(12)}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def hex(length = 10)
|
69
|
+
length.times.map { bool ? random_letter : random_number }.join("")
|
70
|
+
end
|
71
|
+
|
72
|
+
def string_for_regex(pattern)
|
73
|
+
case pattern.to_s
|
74
|
+
when "^(placeholder|placeholder_.+)$"
|
75
|
+
["placeholder", "placeholder_#{WORDS.sample(random: @random)}"].sample(random: @random)
|
76
|
+
when "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"
|
77
|
+
uuid
|
78
|
+
when "^/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?$"
|
79
|
+
base_path
|
80
|
+
when "^[1-9][0-9]{3}[-/](0[1-9]|1[0-2])[-/](0[1-9]|[12][0-9]|3[0-1])$"
|
81
|
+
Date.today.iso8601
|
82
|
+
when "^[1-9][0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[0-1])$"
|
83
|
+
Date.today.iso8601
|
84
|
+
when "^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$"
|
85
|
+
Time.now.strftime("%H:%m")
|
86
|
+
when "^#.+$"
|
87
|
+
anchor
|
88
|
+
when "[a-z-]"
|
89
|
+
random_identifier(separator: "-")
|
90
|
+
when "^[a-z_]+$"
|
91
|
+
random_identifier(separator: "_")
|
92
|
+
when "^/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?$"
|
93
|
+
base_path
|
94
|
+
when "^https://([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[A-Za-z0-9])?\\.)+campaign\\.gov\\.uk(/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?)?$"
|
95
|
+
govuk_subdomain_url
|
96
|
+
when "^https://([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[A-Za-z0-9])?\\.)*gov\\.uk(/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?)?$"
|
97
|
+
govuk_subdomain_url
|
98
|
+
when '[a-z0-9\-_]'
|
99
|
+
"#{hex}-#{hex}"
|
100
|
+
else
|
101
|
+
raise <<-DOC
|
102
|
+
Don't know how to generate random string for pattern #{pattern.inspect}
|
103
|
+
|
104
|
+
This propably means you've introduced a new regex in govuk-content-schemas.
|
105
|
+
Because it's very hard to generate a valid string from a regex alone,
|
106
|
+
we have to specify a method to generate random data for each regex in
|
107
|
+
the schemas.
|
108
|
+
|
109
|
+
To fix this:
|
110
|
+
|
111
|
+
- Add your regex to `lib/govuk_schemas/random.rb`
|
112
|
+
DOC
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
def random_letter
|
119
|
+
letters = ("a".."f").to_a
|
120
|
+
letters[@random.rand(0..letters.count - 1)]
|
121
|
+
end
|
122
|
+
|
123
|
+
def random_number
|
124
|
+
numbers = ("0".."9").to_a
|
125
|
+
numbers[@random.rand(0..numbers.count - 1)]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require "govuk_schemas/
|
2
|
-
require "govuk_schemas/random_item_generator"
|
1
|
+
require "govuk_schemas/random_schema_generator"
|
3
2
|
require "json-schema"
|
4
3
|
require "json"
|
5
4
|
|
@@ -24,11 +23,17 @@ module GovukSchemas
|
|
24
23
|
# schema = GovukSchemas::Schema.find(frontend_schema: "detailed_guide")
|
25
24
|
# GovukSchemas::RandomExample.new(schema: schema).payload
|
26
25
|
#
|
26
|
+
# Example with seed (for consistent results):
|
27
|
+
#
|
28
|
+
# schema = GovukSchemas::Schema.find(frontend_schema: "detailed_guide")
|
29
|
+
# GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload
|
30
|
+
# GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload # returns same as above
|
31
|
+
#
|
27
32
|
# @param [Hash] schema A JSON schema.
|
28
33
|
# @return [GovukSchemas::RandomExample]
|
29
|
-
def initialize(schema:)
|
34
|
+
def initialize(schema:, seed: nil)
|
30
35
|
@schema = schema
|
31
|
-
@random_generator =
|
36
|
+
@random_generator = RandomSchemaGenerator.new(schema: schema, seed: seed)
|
32
37
|
end
|
33
38
|
|
34
39
|
# Returns a new `GovukSchemas::RandomExample` object.
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require "govuk_schemas/
|
1
|
+
require "govuk_schemas/random_content_generator"
|
2
2
|
|
3
3
|
module GovukSchemas
|
4
|
-
# The
|
4
|
+
# The RandomSchemaGenerator takes a JSON schema and outputs a random hash that
|
5
5
|
# is valid against said schema.
|
6
6
|
#
|
7
7
|
# The "randomness" here is quote relative, it's particularly tailored to the
|
@@ -9,9 +9,11 @@ module GovukSchemas
|
|
9
9
|
# hundred characters to keep the resulting items small.
|
10
10
|
#
|
11
11
|
# @private
|
12
|
-
class
|
13
|
-
def initialize(schema:)
|
12
|
+
class RandomSchemaGenerator
|
13
|
+
def initialize(schema:, seed: nil)
|
14
14
|
@schema = schema
|
15
|
+
@random = Random.new(seed || Random.new_seed)
|
16
|
+
@generator = RandomContentGenerator.new(random: @random)
|
15
17
|
end
|
16
18
|
|
17
19
|
def payload
|
@@ -24,45 +26,45 @@ module GovukSchemas
|
|
24
26
|
# TODO: #/definitions/nested_headers are recursively nested and can cause
|
25
27
|
# infinite loops. We need to add something that detects and prevents the
|
26
28
|
# loop. In the meantime return a valid value.
|
27
|
-
if props[
|
29
|
+
if props["$ref"] == "#/definitions/nested_headers"
|
28
30
|
return [{ "text" => "1", "level" => 1, "id" => "ABC" }]
|
29
31
|
end
|
30
32
|
|
31
33
|
# JSON schemas can have "pointers". We use this to extract defintions and
|
32
34
|
# reduce duplication. To make the schema easily parsable we inline the
|
33
35
|
# reference here.
|
34
|
-
if props[
|
35
|
-
props.merge!(lookup_json_pointer(props[
|
36
|
+
if props["$ref"]
|
37
|
+
props.merge!(lookup_json_pointer(props["$ref"]))
|
36
38
|
end
|
37
39
|
|
38
40
|
# Attributes with `enum` specified often omit the `type` from
|
39
41
|
# their definition. It's most likely a string.
|
40
|
-
type = props[
|
42
|
+
type = props["type"] || "string"
|
41
43
|
|
42
44
|
# Except when it has properties, because it's defintely an object then.
|
43
|
-
if props[
|
45
|
+
if props["properties"]
|
44
46
|
type = "object"
|
45
47
|
end
|
46
48
|
|
47
49
|
# Make sure that we choose a type when there are more than one specified.
|
48
|
-
type = Array(type).sample
|
50
|
+
type = Array(type).sample(random: @random)
|
49
51
|
|
50
|
-
if props[
|
51
|
-
generate_value(props[
|
52
|
-
elsif props[
|
52
|
+
if props["anyOf"]
|
53
|
+
generate_value(props["anyOf"].sample(random: @random))
|
54
|
+
elsif props["oneOf"] && type != "object"
|
53
55
|
# FIXME: Generating valid data for a `oneOf` schema is quite interesting.
|
54
56
|
# According to the JSON Schema spec a `oneOf` schema is only valid if
|
55
57
|
# the data is valid against *only one* of the clauses. To do this
|
56
58
|
# properly, we'd have to verify that the data generated below doesn't
|
57
59
|
# validate against the other schemas in `props['oneOf']`.
|
58
|
-
generate_value(props[
|
59
|
-
elsif props[
|
60
|
-
props[
|
60
|
+
generate_value(props["oneOf"].sample(random: @random))
|
61
|
+
elsif props["allOf"]
|
62
|
+
props["allOf"].each_with_object({}) do |subschema, hash|
|
61
63
|
val = generate_value(subschema)
|
62
64
|
hash.merge(val)
|
63
65
|
end
|
64
|
-
elsif props[
|
65
|
-
props[
|
66
|
+
elsif props["enum"]
|
67
|
+
props["enum"].sample(random: @random)
|
66
68
|
elsif type == "null"
|
67
69
|
nil
|
68
70
|
elsif type == "object"
|
@@ -70,11 +72,11 @@ module GovukSchemas
|
|
70
72
|
elsif type == "array"
|
71
73
|
generate_random_array(props)
|
72
74
|
elsif type == "boolean"
|
73
|
-
|
75
|
+
@generator.bool
|
74
76
|
elsif type == "integer"
|
75
|
-
min = props[
|
76
|
-
max = props[
|
77
|
-
rand(min..max)
|
77
|
+
min = props["minimum"] || 0
|
78
|
+
max = props["maximum"] || 10
|
79
|
+
@random.rand(min..max)
|
78
80
|
elsif type == "string"
|
79
81
|
generate_random_string(props)
|
80
82
|
else
|
@@ -85,43 +87,56 @@ module GovukSchemas
|
|
85
87
|
def generate_random_object(subschema)
|
86
88
|
document = {}
|
87
89
|
|
88
|
-
(
|
90
|
+
one_of_sample = subschema.fetch("oneOf", []).sample(random: @random) || {}
|
91
|
+
|
92
|
+
(subschema["properties"] || {}).each do |attribute_name, attribute_properties|
|
89
93
|
# TODO: When the schema contains `subschema['minProperties']` we always
|
90
94
|
# populate all of the keys in the hash. This isn't quite random, but I
|
91
95
|
# haven't found a nice way yet to ensure there's at least n elements in
|
92
96
|
# the hash.
|
93
|
-
|
94
|
-
|
95
|
-
|
97
|
+
should_generate_value = @generator.bool \
|
98
|
+
|| subschema["required"].to_a.include?(attribute_name) \
|
99
|
+
|| (one_of_sample["required"] || {}).to_a.include?(attribute_name) \
|
100
|
+
|| (one_of_sample["properties"] || {}).keys.include?(attribute_name) \
|
101
|
+
|| subschema["minProperties"] \
|
102
|
+
|
103
|
+
next unless should_generate_value
|
104
|
+
|
105
|
+
one_of_properties = (one_of_sample["properties"] || {})[attribute_name]
|
106
|
+
document[attribute_name] = if one_of_properties
|
107
|
+
generate_value(one_of_properties)
|
108
|
+
else
|
109
|
+
generate_value(attribute_properties)
|
110
|
+
end
|
96
111
|
end
|
97
112
|
|
98
113
|
document
|
99
114
|
end
|
100
115
|
|
101
116
|
def generate_random_array(props)
|
102
|
-
min = props[
|
103
|
-
max = props[
|
104
|
-
num_items = rand(min..max)
|
117
|
+
min = props["minItems"] || 0
|
118
|
+
max = props["maxItems"] || 10
|
119
|
+
num_items = @random.rand(min..max)
|
105
120
|
|
106
121
|
num_items.times.map do
|
107
122
|
# sometimes arrays don't have `items` specified, not sure if this is a bug
|
108
|
-
generate_value(props[
|
123
|
+
generate_value(props["items"] || {})
|
109
124
|
end
|
110
125
|
end
|
111
126
|
|
112
127
|
def generate_random_string(props)
|
113
128
|
if props["format"]
|
114
|
-
|
129
|
+
@generator.string_for_type(props["format"])
|
115
130
|
elsif props["pattern"]
|
116
|
-
|
131
|
+
@generator.string_for_regex(props["pattern"])
|
117
132
|
else
|
118
|
-
|
133
|
+
@generator.string(props["minLength"], props["maxLength"])
|
119
134
|
end
|
120
135
|
end
|
121
136
|
|
122
137
|
# Look up a "pointer" like "#/definitions/title" in the schema.
|
123
138
|
def lookup_json_pointer(ref)
|
124
|
-
elements = ref.split(
|
139
|
+
elements = ref.split("/")
|
125
140
|
elements.shift
|
126
141
|
@schema.dig(*elements) || raise("Definition `#{ref}` not found in the schema")
|
127
142
|
end
|
@@ -1,26 +1,16 @@
|
|
1
1
|
module GovukSchemas
|
2
2
|
module RSpecMatchers
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
RSpec::Matchers.define :be_valid_against_links_schema do |schema_name|
|
16
|
-
match do |item|
|
17
|
-
schema = Schema.find(links_schema: schema_name)
|
18
|
-
validator = JSON::Validator.fully_validate(schema, item)
|
19
|
-
validator.empty?
|
20
|
-
end
|
21
|
-
|
22
|
-
failure_message do |actual|
|
23
|
-
ValidationErrorMessage.new(schema_name, "links", actual).message
|
3
|
+
%w[links frontend publisher notification].each do |schema_type|
|
4
|
+
RSpec::Matchers.define "be_valid_against_#{schema_type}_schema".to_sym do |schema_name|
|
5
|
+
match do |item|
|
6
|
+
schema = Schema.find(Hash["#{schema_type}_schema".to_sym, schema_name])
|
7
|
+
validator = JSON::Validator.fully_validate(schema, item)
|
8
|
+
validator.empty?
|
9
|
+
end
|
10
|
+
|
11
|
+
failure_message do |actual|
|
12
|
+
ValidationErrorMessage.new(schema_name, "schema", actual).message
|
13
|
+
end
|
24
14
|
end
|
25
15
|
end
|
26
16
|
end
|
@@ -36,14 +26,14 @@ module GovukSchemas
|
|
36
26
|
end
|
37
27
|
|
38
28
|
def message
|
39
|
-
<<~
|
40
|
-
|
29
|
+
<<~DOC
|
30
|
+
expected the payload to be valid against the '#{schema_name}' schema:
|
41
31
|
|
42
|
-
|
32
|
+
#{formatted_payload}
|
43
33
|
|
44
|
-
|
45
|
-
|
46
|
-
|
34
|
+
Validation errors:
|
35
|
+
#{errors}
|
36
|
+
DOC
|
47
37
|
end
|
48
38
|
|
49
39
|
private
|
@@ -56,6 +46,7 @@ module GovukSchemas
|
|
56
46
|
|
57
47
|
def formatted_payload
|
58
48
|
return payload if payload.is_a?(String)
|
49
|
+
|
59
50
|
JSON.pretty_generate(payload)
|
60
51
|
end
|
61
52
|
|
data/lib/govuk_schemas/schema.rb
CHANGED
@@ -19,11 +19,10 @@ module GovukSchemas
|
|
19
19
|
#
|
20
20
|
# @param schema_type [String] The type: frontend, publisher, notification or links
|
21
21
|
# @return [Array<Hash>] List of JSON schemas as hashes
|
22
|
-
def self.all(schema_type:
|
22
|
+
def self.all(schema_type: "*")
|
23
23
|
schema_type = "publisher_v2" if schema_type == "publisher"
|
24
|
-
Dir.glob("#{GovukSchemas::CONTENT_SCHEMA_DIR}/dist/formats/*/#{schema_type}/*.json").
|
24
|
+
Dir.glob("#{GovukSchemas::CONTENT_SCHEMA_DIR}/dist/formats/*/#{schema_type}/*.json").each_with_object({}) do |file_path, hash|
|
25
25
|
hash[file_path] = JSON.parse(File.read(file_path))
|
26
|
-
hash
|
27
26
|
end
|
28
27
|
end
|
29
28
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: govuk_schemas
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GOV.UK Dev
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json-schema
|
@@ -25,61 +25,61 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.8.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pry-byebug
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '3.4'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '3.4'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: govuk
|
70
|
+
name: rubocop-govuk
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: '3.8'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: '3.8'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: yard
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,6 +101,7 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
+
- ".github/dependabot.yml"
|
104
105
|
- ".gitignore"
|
105
106
|
- ".rspec"
|
106
107
|
- ".rubocop.yml"
|
@@ -117,12 +118,11 @@ files:
|
|
117
118
|
- lib/govuk_schemas.rb
|
118
119
|
- lib/govuk_schemas/document_types.rb
|
119
120
|
- lib/govuk_schemas/example.rb
|
120
|
-
- lib/govuk_schemas/
|
121
|
+
- lib/govuk_schemas/random_content_generator.rb
|
121
122
|
- lib/govuk_schemas/random_example.rb
|
122
|
-
- lib/govuk_schemas/
|
123
|
+
- lib/govuk_schemas/random_schema_generator.rb
|
123
124
|
- lib/govuk_schemas/rspec_matchers.rb
|
124
125
|
- lib/govuk_schemas/schema.rb
|
125
|
-
- lib/govuk_schemas/utils.rb
|
126
126
|
- lib/govuk_schemas/version.rb
|
127
127
|
homepage: https://github.com/alphagov/govuk_schemas_gem
|
128
128
|
licenses:
|
@@ -136,15 +136,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
136
136
|
requirements:
|
137
137
|
- - ">="
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version: 2.
|
139
|
+
version: '2.6'
|
140
140
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - ">="
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '0'
|
145
145
|
requirements: []
|
146
|
-
|
147
|
-
rubygems_version: 2.5.1
|
146
|
+
rubygems_version: 3.1.4
|
148
147
|
signing_key:
|
149
148
|
specification_version: 4
|
150
149
|
summary: Gem to generate test data based on GOV.UK content schemas
|
data/lib/govuk_schemas/random.rb
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
|
3
|
-
module GovukSchemas
|
4
|
-
# @private
|
5
|
-
module Random
|
6
|
-
class << self
|
7
|
-
WORDS = %w[Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut suscipit at mauris non bibendum. Ut ac massa est. Aenean tempor imperdiet leo vel interdum. Nam sagittis cursus sem ultricies scelerisque. Quisque porttitor risus vel risus finibus, eu sollicitudin nisl aliquet. Sed sed lectus ac dolor molestie interdum. Nam molestie pellentesque purus ac vestibulum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse non tempor eros. Mauris eu orci hendrerit, volutpat lorem in, tristique libero. Duis a nibh nibh.].freeze
|
8
|
-
|
9
|
-
def string_for_type(type)
|
10
|
-
if type == 'date-time'
|
11
|
-
time
|
12
|
-
elsif type == 'uri'
|
13
|
-
uri
|
14
|
-
else
|
15
|
-
raise "Unknown attribute type `#{type}`"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def time
|
20
|
-
seconds_ago = rand(10_000_000) - 5_000_000
|
21
|
-
(Time.now + seconds_ago).iso8601
|
22
|
-
end
|
23
|
-
|
24
|
-
# TODO: make this more random with query string, optional anchor.
|
25
|
-
def uri
|
26
|
-
"http://example.com#{base_path}#{anchor}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def base_path
|
30
|
-
"/" + rand(1..5).times.map { SecureRandom.uuid }.join('/')
|
31
|
-
end
|
32
|
-
|
33
|
-
def govuk_subdomain_url
|
34
|
-
subdomain = rand(2..4).times.map {
|
35
|
-
('a'..'z').to_a.sample(rand(3..8)).join
|
36
|
-
}.join('.')
|
37
|
-
"https://#{subdomain}.gov.uk#{base_path}"
|
38
|
-
end
|
39
|
-
|
40
|
-
def string(minimum_chars = nil, maximum_chars = nil)
|
41
|
-
minimum_chars = minimum_chars || 0
|
42
|
-
maximum_chars = maximum_chars || 100
|
43
|
-
WORDS.sample(rand(minimum_chars..maximum_chars)).join(' ')
|
44
|
-
end
|
45
|
-
|
46
|
-
def bool
|
47
|
-
rand(2) == 1
|
48
|
-
end
|
49
|
-
|
50
|
-
def anchor
|
51
|
-
"##{SecureRandom.hex}"
|
52
|
-
end
|
53
|
-
|
54
|
-
def random_identifier(separator:)
|
55
|
-
Utils.parameterize(WORDS.sample(rand(1..10)).join('-')).gsub('-', separator)
|
56
|
-
end
|
57
|
-
|
58
|
-
def string_for_regex(pattern)
|
59
|
-
case pattern.to_s
|
60
|
-
when '^(placeholder|placeholder_.+)$'
|
61
|
-
['placeholder', "placeholder_#{WORDS.sample}"].sample
|
62
|
-
when '^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$'
|
63
|
-
SecureRandom.uuid
|
64
|
-
when "^/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?$"
|
65
|
-
base_path
|
66
|
-
when "^[1-9][0-9]{3}[-/](0[1-9]|1[0-2])[-/](0[1-9]|[12][0-9]|3[0-1])$"
|
67
|
-
Date.today.iso8601
|
68
|
-
when "^[1-9][0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[0-1])$"
|
69
|
-
Date.today.iso8601
|
70
|
-
when "^#.+$"
|
71
|
-
anchor
|
72
|
-
when "[a-z-]"
|
73
|
-
random_identifier(separator: '-')
|
74
|
-
when "^[a-z_]+$"
|
75
|
-
random_identifier(separator: '_')
|
76
|
-
when "^/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?$"
|
77
|
-
base_path
|
78
|
-
when "^https://([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[A-Za-z0-9])?\\.)+campaign\\.gov\\.uk(/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?)?$"
|
79
|
-
govuk_subdomain_url
|
80
|
-
when "^https://([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[A-Za-z0-9])?\\.)*gov\\.uk(/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?)?$"
|
81
|
-
govuk_subdomain_url
|
82
|
-
when '[a-z0-9\-_]'
|
83
|
-
"#{SecureRandom.hex}-#{SecureRandom.hex}"
|
84
|
-
else
|
85
|
-
raise <<-doc
|
86
|
-
Don't know how to generate random string for pattern #{pattern.inspect}
|
87
|
-
|
88
|
-
This propably means you've introduced a new regex in govuk-content-schemas.
|
89
|
-
Because it's very hard to generate a valid string from a regex alone,
|
90
|
-
we have to specify a method to generate random data for each regex in
|
91
|
-
the schemas.
|
92
|
-
|
93
|
-
To fix this:
|
94
|
-
|
95
|
-
- Add your regex to `lib/govuk_schemas/random.rb`
|
96
|
-
doc
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
data/lib/govuk_schemas/utils.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module GovukSchemas
|
2
|
-
# @private
|
3
|
-
module Utils
|
4
|
-
def self.stringify_keys(hash)
|
5
|
-
new_hash = {}
|
6
|
-
hash.each do |k, v|
|
7
|
-
new_hash[k.to_s] = v
|
8
|
-
end
|
9
|
-
new_hash
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.parameterize(string)
|
13
|
-
string.gsub(/[^a-z0-9\-_]+/i, '-')
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|