gitlab-labkit 1.0.1 → 1.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fcf883261bb809a67748d4f15c2a520ae392d3d069b5852cc20abf56e428b4e
4
- data.tar.gz: ce56f2ec2b2dce0b420d0096ea6b4a48b54a91c350b802a245dcc0cae9219908
3
+ metadata.gz: ed4c330d977763fb6063a9caab83352d59f32c46ac10a097f33f468cb06b2ddc
4
+ data.tar.gz: 558fae5ff6684b13cce03ee9b5f306c949a19c1a44b89c3fcae0567caa8175cc
5
5
  SHA512:
6
- metadata.gz: 70d57193fc2778250b004df0e39f7f98ff6f1bc5356d601f664b632cd6a7e9b4f2f772b6251406702b5cea84bda6fed02ad29d1ca196f2b29462e4887ded4b6e
7
- data.tar.gz: 1cd77a020fe80b311512acf08b433913f0e9a06edce05a10961c018c19a1270c9420b477731cb88d4ceb5b641ee0a6d0ad4c34fc1bee528d2febae1f83a521dd
6
+ metadata.gz: f1cd4013a210766f17a988f7922e900b9b00f74089fdde68e143cc718da629bd5d7442248ee47d15d0d7aa513e85eae6d9d642a5ddbfa5eddbe74046f7674cbb
7
+ data.tar.gz: 22dae3a5fb187c7118f4dd86df667982c1e72ef023d5a52aad1c6c10813845f3128d714973957c3ddca2b436b1419115904ca5bd76510315f57a7a8d3cd48078
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "http://json-schema.org/draft-06/schema#",
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/raw/master/config/user_experience_slis/schema.json",
4
4
  "title": "User Experience SLI Definition",
5
5
  "description": "Schema for GitLab User Experience SLI files",
@@ -12,8 +12,8 @@
12
12
  },
13
13
  "feature_category": {
14
14
  "type": "string",
15
- "minLength": 1,
16
- "description": "GitLab feature category this experience belongs to"
15
+ "description": "GitLab feature category this experience belongs to",
16
+ "$ref": "https://gitlab.com/gitlab-org/gitlab/-/raw/master/config/feature_categories/schema.json"
17
17
  },
18
18
  "urgency": {
19
19
  "type": "string",
@@ -25,7 +25,8 @@ Gem::Specification.new do |spec|
25
25
  spec.add_runtime_dependency "grpc", ">= 1.75" # Be sure to update the "grpc-tools" dev_dependency too
26
26
  spec.add_runtime_dependency "google-protobuf", ">= 3.25", "< 5.0"
27
27
  spec.add_runtime_dependency "jaeger-client", "~> 1.1.0"
28
- spec.add_runtime_dependency 'json-schema', '~> 5.1'
28
+ spec.add_runtime_dependency 'json_schemer', '~> 2.4.0'
29
+ spec.add_runtime_dependency "openssl", "~> 3.3.2"
29
30
  spec.add_runtime_dependency "opentracing", "~> 0.4"
30
31
  spec.add_runtime_dependency "pg_query", ">= 6.1.0", "< 7.0"
31
32
  spec.add_runtime_dependency "prometheus-client-mmap", "~> 1.2.9"
@@ -0,0 +1,74 @@
1
+ # JSON Schema Reference Resolver
2
+
3
+ ## Overview
4
+
5
+ The `RefResolver` class provides HTTP/HTTPS-based resolution for external JSON schema references. It enables validation of JSON data against schemas that reference remote schema definitions, with built-in caching and timeout protection.
6
+
7
+ ## Why Use RefResolver?
8
+
9
+ When working with JSON schemas, you may encounter `$ref` properties that point to external schema definitions hosted remotely:
10
+
11
+ ```json
12
+ {
13
+ "$ref": "https://example.com/schemas/common.json#/definitions/address"
14
+ }
15
+ ```
16
+
17
+ The `RefResolver` solves several key challenges:
18
+
19
+ - **Remote Schema Resolution**: Automatically fetches and parses external schema definitions over HTTP/HTTPS
20
+ - **Performance Optimization**: Caches fetched schemas to avoid redundant network requests
21
+ - **Reliability**: Implements configurable timeouts to prevent hanging on slow or unresponsive endpoints
22
+ - **Error Handling**: Provides clear error messages for network failures, timeouts, and invalid JSON responses
23
+
24
+ ## How to Use
25
+
26
+ ### Basic Usage
27
+
28
+ The `RefResolver` is designed to work with the [JSONSchemer](https://github.com/davishmcclurg/json_schemer) gem:
29
+
30
+ ```ruby
31
+ require 'labkit/json_schema/ref_resolver'
32
+
33
+ # Create a resolver instance
34
+ resolver = Labkit::JsonSchema::RefResolver.new
35
+
36
+ # Use with JSONSchemer
37
+ schema = JSONSchemer.schema(
38
+ your_schema_hash,
39
+ ref_resolver: resolver
40
+ )
41
+
42
+ # Validate data
43
+ schema.valid?(your_data)
44
+ ```
45
+
46
+ ### Custom Timeout Configuration
47
+
48
+ By default, the resolver uses a 2-second timeout for both connection and read operations. You can customize this:
49
+
50
+ ```ruby
51
+ # Use a 5-second timeout
52
+ resolver = Labkit::JsonSchema::RefResolver.new(timeout_s: 5)
53
+ ```
54
+
55
+ ### Cache Management
56
+
57
+ The resolver maintains a class-level cache to share fetched schemas across instances:
58
+
59
+ ```ruby
60
+ # Access the cache
61
+ Labkit::JsonSchema::RefResolver.cache
62
+
63
+ # Clear the cache if needed
64
+ Labkit::JsonSchema::RefResolver.cache.clear
65
+ ```
66
+
67
+ ## Use Case Example
68
+
69
+ The `RefResolver` is used in the User Experience SLI Registry [lib/labkit/user_experience_sli/registry.rb](lib/labkit/user_experience_sli/registry.rb) to validate user experience definitions against a JSON schema.
70
+
71
+ ## Implementation Details
72
+
73
+ - Only HTTP and HTTPS schemes are supported. Other schemes (FTP, file://, etc.) will raise an error.
74
+ - The class-level cache is shared across all instances. In multi-threaded environments, consider using appropriate synchronization mechanisms if cache consistency is critical.
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module Labkit
8
+ module JsonSchema
9
+ # This class resolves JSON schema references (e.g., "$ref": "http://example.com/schema.json")
10
+ # by fetching remote schemas over HTTP/HTTPS and caching them.
11
+ # It is used by JSONSchemer to resolve external schema definitions.
12
+ # We need it to validate JSON data against schemas that might be hosted remotely.
13
+ class RefResolver
14
+ mattr_accessor :cache, default: {}
15
+
16
+ def initialize(timeout_s: 2)
17
+ @timeout_s = timeout_s
18
+ end
19
+
20
+ def call(uri)
21
+ uri_str = uri.to_s
22
+
23
+ return cache[uri_str] if cache.key?(uri_str)
24
+
25
+ cache[uri_str] = fetch_remote_schema(uri_str)
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_remote_schema(uri_str)
31
+ uri = URI(uri_str)
32
+
33
+ raise(JSONSchemer::UnknownRef, "Unsupported URI scheme: #{uri_str}") unless %w[http https].include?(uri.scheme)
34
+
35
+ response = Net::HTTP.start(
36
+ uri.host,
37
+ uri.port,
38
+ use_ssl: uri.scheme == 'https',
39
+ open_timeout: @timeout_s,
40
+ read_timeout: @timeout_s
41
+ ) do |http|
42
+ request = Net::HTTP::Get.new(uri.request_uri)
43
+ http.request(request)
44
+ end
45
+
46
+ unless response.is_a?(Net::HTTPSuccess)
47
+ raise(
48
+ JSONSchemer::UnknownRef,
49
+ "Failed to fetch #{uri_str}: #{response.code} #{response.message}"
50
+ )
51
+ end
52
+
53
+ JSON.parse(response.body)
54
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
55
+ raise(JSONSchemer::UnknownRef, "Timeout fetching #{uri_str}: #{e.message}")
56
+ rescue JSON::ParserError => e
57
+ raise(JSONSchemer::UnknownRef, "Invalid JSON at #{uri_str}: #{e.message}")
58
+ rescue SocketError, Errno::ECONNREFUSED => e
59
+ raise(JSONSchemer::UnknownRef, "Connection failed for #{uri_str}: #{e.message}")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -33,6 +33,23 @@ This allows you to:
33
33
 
34
34
  **Note:** The registry is automatically reset when the configuration changes, so the new path takes effect immediately.
35
35
 
36
+ ### RefResolver Timeout Configuration
37
+
38
+ When validating user experience SLI definitions against JSON schemas, the system may need to fetch remote schema references over HTTP/HTTPS. By default, the timeout for these requests is 2 seconds. You can configure a custom timeout:
39
+
40
+ ```ruby
41
+ Labkit::UserExperienceSli.configure do |config|
42
+ config.ref_resolver_timeout = 5 # Set timeout to 5 seconds
43
+ end
44
+ ```
45
+
46
+ This configuration is useful when:
47
+ - Working in environments with slower network connections
48
+ - Fetching schemas from remote servers with higher latency
49
+ - Needing stricter timeout constraints for faster failure detection
50
+
51
+ **Note:** The timeout applies to both connection opening and reading operations when fetching remote JSON schema references.
52
+
36
53
  ### User Experience Definitions
37
54
 
38
55
  User Experience SLI definitions will be lazy loaded from the default directory (`config/user_experience_slis`).
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require 'json-schema'
4
+ require 'json_schemer'
5
5
  require 'pathname'
6
6
  require 'yaml'
7
+ require 'labkit/json_schema/ref_resolver'
7
8
 
8
9
  module Labkit
9
10
  module UserExperienceSli
@@ -77,8 +78,7 @@ module Labkit
77
78
  content = YAML.safe_load(file_path.read)
78
79
  return nil unless content.is_a?(Hash)
79
80
 
80
- errors = JSON::Validator.fully_validate(schema, content)
81
- return Definition.new(user_experience_id: experience_id, **content) if errors.empty?
81
+ return Definition.new(user_experience_id: experience_id, **content) if schema.valid?(content)
82
82
 
83
83
  warn("Invalid schema for #{file_path}")
84
84
 
@@ -90,7 +90,11 @@ module Labkit
90
90
  end
91
91
 
92
92
  def schema
93
- @schema ||= JSON.parse(File.read(SCHEMA_PATH))
93
+ @schema ||= begin
94
+ schema = JSON.parse(File.read(SCHEMA_PATH))
95
+ timeout = Labkit::UserExperienceSli.configuration.ref_resolver_timeout
96
+ JSONSchemer.schema(schema, ref_resolver: Labkit::JsonSchema::RefResolver.new(timeout_s: timeout))
97
+ end
94
98
  end
95
99
 
96
100
  def warn(message)
@@ -16,11 +16,12 @@ module Labkit
16
16
  module UserExperienceSli
17
17
  # Configuration class for UserExperienceSli
18
18
  class Configuration
19
- attr_accessor :logger, :registry_path
19
+ attr_accessor :logger, :registry_path, :ref_resolver_timeout
20
20
 
21
21
  def initialize
22
22
  @logger = Labkit::Logging::JsonLogger.new($stdout)
23
23
  @registry_path = File.join("config", "user_experience_slis")
24
+ @ref_resolver_timeout = 2
24
25
  end
25
26
  end
26
27
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-labkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Newdigate
@@ -98,19 +98,33 @@ dependencies:
98
98
  - !ruby/object:Gem::Version
99
99
  version: 1.1.0
100
100
  - !ruby/object:Gem::Dependency
101
- name: json-schema
101
+ name: json_schemer
102
102
  requirement: !ruby/object:Gem::Requirement
103
103
  requirements:
104
104
  - - "~>"
105
105
  - !ruby/object:Gem::Version
106
- version: '5.1'
106
+ version: 2.4.0
107
107
  type: :runtime
108
108
  prerelease: false
109
109
  version_requirements: !ruby/object:Gem::Requirement
110
110
  requirements:
111
111
  - - "~>"
112
112
  - !ruby/object:Gem::Version
113
- version: '5.1'
113
+ version: 2.4.0
114
+ - !ruby/object:Gem::Dependency
115
+ name: openssl
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: 3.3.2
121
+ type: :runtime
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: 3.3.2
114
128
  - !ruby/object:Gem::Dependency
115
129
  name: opentracing
116
130
  requirement: !ruby/object:Gem::Requirement
@@ -468,6 +482,8 @@ files:
468
482
  - lib/labkit/fields.rb
469
483
  - lib/labkit/fips.rb
470
484
  - lib/labkit/httpclient_publisher.rb
485
+ - lib/labkit/json_schema/README.md
486
+ - lib/labkit/json_schema/ref_resolver.rb
471
487
  - lib/labkit/logging.rb
472
488
  - lib/labkit/logging/grpc.rb
473
489
  - lib/labkit/logging/grpc/server_interceptor.rb