synthra 0.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 +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- metadata +478 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "openssl"
|
|
6
|
+
|
|
7
|
+
module Synthra
|
|
8
|
+
# Webhook Simulation
|
|
9
|
+
#
|
|
10
|
+
# Simulate webhook deliveries for testing integrations with
|
|
11
|
+
# payment providers, notification services, and third-party APIs.
|
|
12
|
+
#
|
|
13
|
+
# @example Configure and send webhook
|
|
14
|
+
# Synthra::WebhookSimulator.configure do |config|
|
|
15
|
+
# config.endpoint = "http://localhost:3000/webhooks/stripe"
|
|
16
|
+
# config.secret = "whsec_test123"
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Synthra::WebhookSimulator.send(
|
|
20
|
+
# "payment_intent.succeeded",
|
|
21
|
+
# data: { amount: 2000, currency: "usd" }
|
|
22
|
+
# )
|
|
23
|
+
#
|
|
24
|
+
module WebhookSimulator
|
|
25
|
+
class << self
|
|
26
|
+
attr_accessor :configuration
|
|
27
|
+
|
|
28
|
+
def configure
|
|
29
|
+
self.configuration ||= Configuration.new
|
|
30
|
+
yield(configuration) if block_given?
|
|
31
|
+
configuration
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Send a webhook
|
|
35
|
+
#
|
|
36
|
+
# @param event_type [String] event type (e.g., "payment_intent.succeeded")
|
|
37
|
+
# @param data [Hash] event data
|
|
38
|
+
# @param options [Hash] delivery options
|
|
39
|
+
# @option options [String] :endpoint override default endpoint
|
|
40
|
+
# @option options [String] :secret override default secret
|
|
41
|
+
# @option options [Integer] :delay delay in seconds before sending
|
|
42
|
+
# @option options [String] :schema schema to generate data from
|
|
43
|
+
# @option options [Hash] :headers additional headers
|
|
44
|
+
# @return [WebhookResult] delivery result
|
|
45
|
+
#
|
|
46
|
+
def send(event_type, data: nil, **options)
|
|
47
|
+
config = configuration || Configuration.new
|
|
48
|
+
endpoint = options[:endpoint] || config.endpoint
|
|
49
|
+
secret = options[:secret] || config.secret
|
|
50
|
+
|
|
51
|
+
raise ConfigurationError, "Endpoint not configured" unless endpoint
|
|
52
|
+
|
|
53
|
+
# Generate data from schema if specified
|
|
54
|
+
if options[:schema] && data.nil?
|
|
55
|
+
schema = Synthra.registry.schema(options[:schema].to_s)
|
|
56
|
+
data = schema.generate
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Build webhook payload
|
|
60
|
+
payload = build_payload(event_type, data, config)
|
|
61
|
+
|
|
62
|
+
# Handle delay
|
|
63
|
+
if options[:delay]
|
|
64
|
+
Thread.new do
|
|
65
|
+
sleep(options[:delay])
|
|
66
|
+
deliver(endpoint, payload, secret, options[:headers])
|
|
67
|
+
end
|
|
68
|
+
return WebhookResult.new(
|
|
69
|
+
success: true,
|
|
70
|
+
status: :scheduled,
|
|
71
|
+
message: "Webhook scheduled for delivery in #{options[:delay]}s"
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Deliver immediately
|
|
76
|
+
deliver(endpoint, payload, secret, options[:headers])
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Send webhook asynchronously
|
|
80
|
+
#
|
|
81
|
+
# @param event_type [String] event type
|
|
82
|
+
# @param options [Hash] options
|
|
83
|
+
# @return [Thread] delivery thread
|
|
84
|
+
#
|
|
85
|
+
def send_async(event_type, **options)
|
|
86
|
+
Thread.new { send(event_type, **options) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Simulate a sequence of webhooks
|
|
90
|
+
#
|
|
91
|
+
# @param events [Array<Hash>] list of events with delays
|
|
92
|
+
# @yield [result] for each delivered webhook
|
|
93
|
+
# @return [Array<WebhookResult>] results
|
|
94
|
+
#
|
|
95
|
+
# @example Simulate payment flow
|
|
96
|
+
# WebhookSimulator.sequence([
|
|
97
|
+
# { type: "payment_intent.created", delay: 0 },
|
|
98
|
+
# { type: "payment_intent.processing", delay: 1 },
|
|
99
|
+
# { type: "payment_intent.succeeded", delay: 2 }
|
|
100
|
+
# ])
|
|
101
|
+
#
|
|
102
|
+
def sequence(events, &block)
|
|
103
|
+
results = []
|
|
104
|
+
|
|
105
|
+
events.each do |event|
|
|
106
|
+
sleep(event[:delay] || 0) if event[:delay]
|
|
107
|
+
|
|
108
|
+
result = send(
|
|
109
|
+
event[:type],
|
|
110
|
+
data: event[:data],
|
|
111
|
+
schema: event[:schema],
|
|
112
|
+
endpoint: event[:endpoint]
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
results << result
|
|
116
|
+
yield result if block_given?
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
results
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Register a webhook template
|
|
123
|
+
#
|
|
124
|
+
# @param name [Symbol] template name
|
|
125
|
+
# @param event_type [String] event type
|
|
126
|
+
# @param schema [String] schema for data
|
|
127
|
+
# @param options [Hash] default options
|
|
128
|
+
#
|
|
129
|
+
def register_template(name, event_type:, schema: nil, **options)
|
|
130
|
+
templates[name] = {
|
|
131
|
+
event_type: event_type,
|
|
132
|
+
schema: schema,
|
|
133
|
+
options: options
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Send webhook using template
|
|
138
|
+
#
|
|
139
|
+
# @param template_name [Symbol] registered template name
|
|
140
|
+
# @param overrides [Hash] data overrides
|
|
141
|
+
# @return [WebhookResult]
|
|
142
|
+
#
|
|
143
|
+
def from_template(template_name, **overrides)
|
|
144
|
+
template = templates[template_name]
|
|
145
|
+
raise ArgumentError, "Unknown template: #{template_name}" unless template
|
|
146
|
+
|
|
147
|
+
send(
|
|
148
|
+
template[:event_type],
|
|
149
|
+
schema: template[:schema],
|
|
150
|
+
**template[:options].merge(overrides)
|
|
151
|
+
)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Templates registry
|
|
155
|
+
def templates
|
|
156
|
+
@templates ||= {}
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def build_payload(event_type, data, config)
|
|
162
|
+
{
|
|
163
|
+
id: "evt_#{SecureRandom.hex(12)}",
|
|
164
|
+
type: event_type,
|
|
165
|
+
created: Time.now.to_i,
|
|
166
|
+
data: {
|
|
167
|
+
object: data || {}
|
|
168
|
+
},
|
|
169
|
+
livemode: false,
|
|
170
|
+
api_version: config.api_version
|
|
171
|
+
}
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def deliver(endpoint, payload, secret, extra_headers)
|
|
175
|
+
uri = URI.parse(endpoint)
|
|
176
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
177
|
+
http.use_ssl = uri.scheme == "https"
|
|
178
|
+
http.open_timeout = 10
|
|
179
|
+
http.read_timeout = 30
|
|
180
|
+
|
|
181
|
+
body = JSON.generate(payload)
|
|
182
|
+
|
|
183
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
|
184
|
+
request["Content-Type"] = "application/json"
|
|
185
|
+
request["User-Agent"] = "Synthra-Webhook/1.0"
|
|
186
|
+
|
|
187
|
+
# Add signature if secret provided
|
|
188
|
+
if secret
|
|
189
|
+
timestamp = Time.now.to_i
|
|
190
|
+
signature = compute_signature(timestamp, body, secret)
|
|
191
|
+
request["Stripe-Signature"] = "t=#{timestamp},v1=#{signature}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Add extra headers
|
|
195
|
+
extra_headers&.each { |k, v| request[k] = v }
|
|
196
|
+
|
|
197
|
+
request.body = body
|
|
198
|
+
|
|
199
|
+
response = http.request(request)
|
|
200
|
+
|
|
201
|
+
WebhookResult.new(
|
|
202
|
+
success: response.code.to_i < 400,
|
|
203
|
+
status: response.code.to_i,
|
|
204
|
+
body: response.body,
|
|
205
|
+
payload: payload
|
|
206
|
+
)
|
|
207
|
+
rescue StandardError => e
|
|
208
|
+
WebhookResult.new(
|
|
209
|
+
success: false,
|
|
210
|
+
status: 0,
|
|
211
|
+
error: e.message,
|
|
212
|
+
payload: payload
|
|
213
|
+
)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def compute_signature(timestamp, payload, secret)
|
|
217
|
+
signed_payload = "#{timestamp}.#{payload}"
|
|
218
|
+
OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Configuration class
|
|
223
|
+
class Configuration
|
|
224
|
+
attr_accessor :endpoint, :secret, :api_version
|
|
225
|
+
attr_accessor :default_headers, :timeout
|
|
226
|
+
|
|
227
|
+
def initialize
|
|
228
|
+
@endpoint = nil
|
|
229
|
+
@secret = nil
|
|
230
|
+
@api_version = "2023-10-16"
|
|
231
|
+
@default_headers = {}
|
|
232
|
+
@timeout = 30
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Webhook delivery result
|
|
237
|
+
class WebhookResult
|
|
238
|
+
attr_reader :success, :status, :body, :error, :payload
|
|
239
|
+
|
|
240
|
+
def initialize(success:, status:, body: nil, error: nil, payload: nil, message: nil)
|
|
241
|
+
@success = success
|
|
242
|
+
@status = status
|
|
243
|
+
@body = body
|
|
244
|
+
@error = error
|
|
245
|
+
@payload = payload
|
|
246
|
+
@message = message
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def success?
|
|
250
|
+
@success
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def failed?
|
|
254
|
+
!@success
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def to_h
|
|
258
|
+
{
|
|
259
|
+
success: @success,
|
|
260
|
+
status: @status,
|
|
261
|
+
body: @body,
|
|
262
|
+
error: @error
|
|
263
|
+
}
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
class ConfigurationError < StandardError; end
|
|
268
|
+
|
|
269
|
+
# Simulate Stripe webhooks
|
|
270
|
+
module Stripe
|
|
271
|
+
EVENTS = {
|
|
272
|
+
payment_succeeded: "payment_intent.succeeded",
|
|
273
|
+
payment_failed: "payment_intent.payment_failed",
|
|
274
|
+
subscription_created: "customer.subscription.created",
|
|
275
|
+
subscription_updated: "customer.subscription.updated",
|
|
276
|
+
subscription_deleted: "customer.subscription.deleted",
|
|
277
|
+
invoice_paid: "invoice.paid",
|
|
278
|
+
invoice_payment_failed: "invoice.payment_failed",
|
|
279
|
+
charge_succeeded: "charge.succeeded",
|
|
280
|
+
charge_refunded: "charge.refunded"
|
|
281
|
+
}.freeze
|
|
282
|
+
|
|
283
|
+
class << self
|
|
284
|
+
def payment_succeeded(amount:, currency: "usd", **options)
|
|
285
|
+
WebhookSimulator.send("payment_intent.succeeded", data: {
|
|
286
|
+
id: "pi_#{SecureRandom.hex(12)}",
|
|
287
|
+
object: "payment_intent",
|
|
288
|
+
amount: amount,
|
|
289
|
+
currency: currency,
|
|
290
|
+
status: "succeeded",
|
|
291
|
+
created: Time.now.to_i
|
|
292
|
+
}, **options)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def subscription_created(customer_id:, plan_id:, **options)
|
|
296
|
+
WebhookSimulator.send("customer.subscription.created", data: {
|
|
297
|
+
id: "sub_#{SecureRandom.hex(12)}",
|
|
298
|
+
object: "subscription",
|
|
299
|
+
customer: customer_id,
|
|
300
|
+
items: { data: [{ price: { id: plan_id } }] },
|
|
301
|
+
status: "active",
|
|
302
|
+
created: Time.now.to_i
|
|
303
|
+
}, **options)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Simulate GitHub webhooks
|
|
309
|
+
module GitHub
|
|
310
|
+
EVENTS = {
|
|
311
|
+
push: "push",
|
|
312
|
+
pull_request: "pull_request",
|
|
313
|
+
issue: "issues",
|
|
314
|
+
release: "release"
|
|
315
|
+
}.freeze
|
|
316
|
+
|
|
317
|
+
class << self
|
|
318
|
+
def push(repo:, ref: "refs/heads/main", **options)
|
|
319
|
+
WebhookSimulator.send("push", data: {
|
|
320
|
+
ref: ref,
|
|
321
|
+
repository: { full_name: repo },
|
|
322
|
+
commits: [],
|
|
323
|
+
pusher: { name: "user", email: "user@example.com" }
|
|
324
|
+
}, **options)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def pull_request(repo:, action: "opened", **options)
|
|
328
|
+
WebhookSimulator.send("pull_request", data: {
|
|
329
|
+
action: action,
|
|
330
|
+
number: rand(1..1000),
|
|
331
|
+
repository: { full_name: repo },
|
|
332
|
+
pull_request: {
|
|
333
|
+
title: "Test PR",
|
|
334
|
+
state: action == "closed" ? "closed" : "open"
|
|
335
|
+
}
|
|
336
|
+
}, **options)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
data/lib/synthra.rb
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Synthra - Generate fake data from a human-friendly DSL
|
|
5
|
+
# =============================================================================
|
|
6
|
+
#
|
|
7
|
+
# A Ruby gem for defining data schemas using a simple DSL and generating
|
|
8
|
+
# realistic fake data with support for:
|
|
9
|
+
#
|
|
10
|
+
# - 50+ built-in types (uuid, name, email, etc.)
|
|
11
|
+
# - Schema inheritance
|
|
12
|
+
# - Cross-schema references
|
|
13
|
+
# - Behaviors (latency, failure simulation)
|
|
14
|
+
# - Multiple generation modes (random, edge, invalid, hostile)
|
|
15
|
+
# - Export to multiple formats (TypeScript, Python, GraphQL, etc.)
|
|
16
|
+
#
|
|
17
|
+
# @example Basic usage
|
|
18
|
+
# schema = Synthra.load("schemas/user.dsl")
|
|
19
|
+
# user = schema.generate
|
|
20
|
+
#
|
|
21
|
+
# @author Synthra Contributors
|
|
22
|
+
# @see https://github.com/talaatmagdyx/synthra
|
|
23
|
+
#
|
|
24
|
+
# =============================================================================
|
|
25
|
+
|
|
26
|
+
require "zeitwerk"
|
|
27
|
+
require "faker"
|
|
28
|
+
require "json"
|
|
29
|
+
require "securerandom"
|
|
30
|
+
|
|
31
|
+
# Configure Zeitwerk autoloader
|
|
32
|
+
loader = Zeitwerk::Loader.for_gem
|
|
33
|
+
loader.inflector.inflect(
|
|
34
|
+
"synthra" => "Synthra",
|
|
35
|
+
"dsl" => "DSL",
|
|
36
|
+
"rng" => "RNG",
|
|
37
|
+
"ast" => "AST",
|
|
38
|
+
"cli" => "CLI",
|
|
39
|
+
"lsp" => "LSP",
|
|
40
|
+
"repl" => "REPL",
|
|
41
|
+
"api" => "API"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Ignore domain folders (manually loaded for flat namespace)
|
|
45
|
+
IGNORED_DOMAIN_FOLDERS = %w[
|
|
46
|
+
address_location finance_banking technology_internet commerce_products
|
|
47
|
+
media_entertainment personal_names text_content health_medical travel
|
|
48
|
+
date_time crypto core
|
|
49
|
+
].freeze
|
|
50
|
+
|
|
51
|
+
# Ignore deprecated files and domain folders
|
|
52
|
+
%w[
|
|
53
|
+
address_location_types finance_banking_types technology_internet_types
|
|
54
|
+
commerce_product_types media_entertainment_types personal_names_types
|
|
55
|
+
text_content_types datetime hash time
|
|
56
|
+
].each { |f| loader.ignore("#{__dir__}/synthra/types/#{f}.rb") }
|
|
57
|
+
|
|
58
|
+
IGNORED_DOMAIN_FOLDERS.each do |folder|
|
|
59
|
+
loader.ignore("#{__dir__}/synthra/types/#{folder}")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Ignore manually loaded modules
|
|
63
|
+
loader.ignore("#{__dir__}/synthra/utils")
|
|
64
|
+
loader.ignore("#{__dir__}/synthra/validator")
|
|
65
|
+
loader.ignore("#{__dir__}/synthra/lsp")
|
|
66
|
+
loader.ignore("#{__dir__}/synthra/repl")
|
|
67
|
+
loader.ignore("#{__dir__}/synthra/cli/commands")
|
|
68
|
+
loader.ignore("#{__dir__}/synthra/types/hostile_payloads.rb")
|
|
69
|
+
|
|
70
|
+
# Ignore new feature files (manually loaded)
|
|
71
|
+
loader.ignore("#{__dir__}/synthra/api.rb")
|
|
72
|
+
loader.ignore("#{__dir__}/synthra/type_definitions.rb")
|
|
73
|
+
loader.ignore("#{__dir__}/synthra/config_file.rb")
|
|
74
|
+
loader.ignore("#{__dir__}/synthra/schema_inheritance.rb")
|
|
75
|
+
loader.ignore("#{__dir__}/synthra/factory_bot_integration.rb")
|
|
76
|
+
loader.ignore("#{__dir__}/synthra/snapshot_testing.rb")
|
|
77
|
+
loader.ignore("#{__dir__}/synthra/export/type_mapping.rb")
|
|
78
|
+
loader.ignore("#{__dir__}/synthra/export/graphql.rb")
|
|
79
|
+
|
|
80
|
+
# Ignore Phase 2 feature files (manually loaded)
|
|
81
|
+
loader.ignore("#{__dir__}/synthra/rails_test_helper.rb")
|
|
82
|
+
loader.ignore("#{__dir__}/synthra/mixin.rb")
|
|
83
|
+
loader.ignore("#{__dir__}/synthra/relationships.rb")
|
|
84
|
+
loader.ignore("#{__dir__}/synthra/schema_versioning.rb")
|
|
85
|
+
loader.ignore("#{__dir__}/synthra/openapi_importer.rb")
|
|
86
|
+
loader.ignore("#{__dir__}/synthra/database_seeder.rb")
|
|
87
|
+
loader.ignore("#{__dir__}/synthra/live_preview.rb")
|
|
88
|
+
loader.ignore("#{__dir__}/synthra/documentation_generator.rb")
|
|
89
|
+
loader.ignore("#{__dir__}/synthra/deterministic_ids.rb")
|
|
90
|
+
|
|
91
|
+
# Ignore Production-grade features (manually loaded)
|
|
92
|
+
loader.ignore("#{__dir__}/synthra/export/protobuf.rb")
|
|
93
|
+
loader.ignore("#{__dir__}/synthra/export/openapi.rb")
|
|
94
|
+
loader.ignore("#{__dir__}/synthra/mock_server.rb")
|
|
95
|
+
loader.ignore("#{__dir__}/synthra/contracts_registry.rb")
|
|
96
|
+
loader.ignore("#{__dir__}/synthra/performance_mode.rb")
|
|
97
|
+
loader.ignore("#{__dir__}/synthra/api_server.rb")
|
|
98
|
+
|
|
99
|
+
# Ignore Rails-tier features (manually loaded)
|
|
100
|
+
loader.ignore("#{__dir__}/synthra/engine.rb")
|
|
101
|
+
loader.ignore("#{__dir__}/synthra/activerecord_inference.rb")
|
|
102
|
+
loader.ignore("#{__dir__}/synthra/scenarios.rb")
|
|
103
|
+
loader.ignore("#{__dir__}/synthra/personas.rb")
|
|
104
|
+
loader.ignore("#{__dir__}/synthra/time_travel.rb")
|
|
105
|
+
loader.ignore("#{__dir__}/synthra/quality_metrics.rb")
|
|
106
|
+
loader.ignore("#{__dir__}/synthra/webhook_simulator.rb")
|
|
107
|
+
loader.ignore("#{__dir__}/synthra/migration_generator.rb")
|
|
108
|
+
loader.ignore("#{__dir__}/synthra/export/graphql_federation.rb")
|
|
109
|
+
loader.ignore("#{__dir__}/synthra/export/terraform.rb")
|
|
110
|
+
|
|
111
|
+
# Ignore Rails generators (loaded by Rails when available)
|
|
112
|
+
loader.ignore("#{__dir__}/generators")
|
|
113
|
+
|
|
114
|
+
# Ignore initializer and loader_config (loaded manually, not a class/module)
|
|
115
|
+
loader.ignore("#{__dir__}/synthra/initializer.rb")
|
|
116
|
+
loader.ignore("#{__dir__}/synthra/loader_config.rb")
|
|
117
|
+
|
|
118
|
+
loader.setup
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# Manual requires for organized modules
|
|
122
|
+
# =============================================================================
|
|
123
|
+
|
|
124
|
+
# Core utilities
|
|
125
|
+
require_relative "synthra/utils/string_distance"
|
|
126
|
+
require_relative "synthra/validator/path_validator"
|
|
127
|
+
require_relative "synthra/validator/dsl_validator"
|
|
128
|
+
|
|
129
|
+
# Export system
|
|
130
|
+
require_relative "synthra/export"
|
|
131
|
+
require_relative "synthra/export/type_mapping"
|
|
132
|
+
|
|
133
|
+
# LSP and REPL
|
|
134
|
+
require_relative "synthra/lsp/server"
|
|
135
|
+
require_relative "synthra/repl/formatter"
|
|
136
|
+
require_relative "synthra/repl/enhanced_repl"
|
|
137
|
+
|
|
138
|
+
# Hostile payloads
|
|
139
|
+
require_relative "synthra/types/hostile_payloads"
|
|
140
|
+
|
|
141
|
+
# New features
|
|
142
|
+
require_relative "synthra/api"
|
|
143
|
+
require_relative "synthra/type_definitions"
|
|
144
|
+
require_relative "synthra/config_file"
|
|
145
|
+
require_relative "synthra/schema_inheritance"
|
|
146
|
+
require_relative "synthra/factory_bot_integration"
|
|
147
|
+
require_relative "synthra/snapshot_testing"
|
|
148
|
+
|
|
149
|
+
# Phase 2 features
|
|
150
|
+
require_relative "synthra/rails_test_helper"
|
|
151
|
+
require_relative "synthra/mixin"
|
|
152
|
+
require_relative "synthra/relationships"
|
|
153
|
+
require_relative "synthra/schema_versioning"
|
|
154
|
+
require_relative "synthra/openapi_importer"
|
|
155
|
+
require_relative "synthra/database_seeder"
|
|
156
|
+
require_relative "synthra/live_preview"
|
|
157
|
+
require_relative "synthra/documentation_generator"
|
|
158
|
+
require_relative "synthra/deterministic_ids"
|
|
159
|
+
|
|
160
|
+
# Production-grade features
|
|
161
|
+
require_relative "synthra/export/protobuf"
|
|
162
|
+
require_relative "synthra/export/openapi"
|
|
163
|
+
require_relative "synthra/mock_server"
|
|
164
|
+
require_relative "synthra/contracts_registry"
|
|
165
|
+
require_relative "synthra/performance_mode"
|
|
166
|
+
require_relative "synthra/api_server"
|
|
167
|
+
|
|
168
|
+
# Rails-tier features
|
|
169
|
+
require_relative "synthra/time_travel"
|
|
170
|
+
require_relative "synthra/scenarios"
|
|
171
|
+
require_relative "synthra/personas"
|
|
172
|
+
require_relative "synthra/quality_metrics"
|
|
173
|
+
require_relative "synthra/webhook_simulator"
|
|
174
|
+
require_relative "synthra/migration_generator"
|
|
175
|
+
require_relative "synthra/export/graphql_federation"
|
|
176
|
+
require_relative "synthra/export/terraform"
|
|
177
|
+
|
|
178
|
+
# Rails Engine (only if Rails is available)
|
|
179
|
+
# :nocov: — load-time, Rails-only. The gem's RSpec suite runs without Rails, so
|
|
180
|
+
# Rails::Engine is never defined and this require block cannot execute under test.
|
|
181
|
+
if defined?(Rails::Engine)
|
|
182
|
+
require_relative "synthra/engine"
|
|
183
|
+
require_relative "synthra/activerecord_inference"
|
|
184
|
+
end
|
|
185
|
+
# :nocov:
|
|
186
|
+
|
|
187
|
+
# CLI command pattern
|
|
188
|
+
require_relative "synthra/cli/commands/base"
|
|
189
|
+
require_relative "synthra/cli/commands/validate"
|
|
190
|
+
require_relative "synthra/cli/commands/lint"
|
|
191
|
+
require_relative "synthra/cli/commands/generate"
|
|
192
|
+
require_relative "synthra/cli/commands/export"
|
|
193
|
+
require_relative "synthra/cli/commands/diff"
|
|
194
|
+
require_relative "synthra/cli/commands/import"
|
|
195
|
+
require_relative "synthra/cli/commands/docs"
|
|
196
|
+
require_relative "synthra/cli/commands/live"
|
|
197
|
+
require_relative "synthra/cli/commands/seed"
|
|
198
|
+
|
|
199
|
+
# =============================================================================
|
|
200
|
+
# Main Module
|
|
201
|
+
# =============================================================================
|
|
202
|
+
|
|
203
|
+
module Synthra
|
|
204
|
+
# Probability that an optional field will be included (80%)
|
|
205
|
+
OPTIONAL_FIELD_PRESENCE_RATE = 0.8
|
|
206
|
+
|
|
207
|
+
# Probability that a nullable field will be null (10%)
|
|
208
|
+
NULLABLE_FIELD_NULL_RATE = 0.1
|
|
209
|
+
|
|
210
|
+
# Maximum DSL file size to prevent DoS (10MB)
|
|
211
|
+
MAX_DSL_FILE_SIZE = 10 * 1024 * 1024
|
|
212
|
+
|
|
213
|
+
# Extend with API methods
|
|
214
|
+
extend API
|
|
215
|
+
|
|
216
|
+
class << self
|
|
217
|
+
# Load configuration from config file on first use
|
|
218
|
+
#
|
|
219
|
+
# @return [void]
|
|
220
|
+
#
|
|
221
|
+
def load_config_file!
|
|
222
|
+
@config_loaded ||= begin
|
|
223
|
+
ConfigFile.load_and_apply
|
|
224
|
+
true
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# =============================================================================
|
|
231
|
+
# Eager loading and initialization
|
|
232
|
+
# =============================================================================
|
|
233
|
+
|
|
234
|
+
# Eager load all autoloaded files
|
|
235
|
+
loader.eager_load
|
|
236
|
+
|
|
237
|
+
# Load domain folder type files
|
|
238
|
+
IGNORED_DOMAIN_FOLDERS.each do |folder|
|
|
239
|
+
dir = File.join(__dir__, "synthra/types/#{folder}")
|
|
240
|
+
# :nocov: — load-time guard. Every listed domain folder ships with the gem, so
|
|
241
|
+
# the missing-directory (`next`) arm is never taken once the gem is installed.
|
|
242
|
+
next unless Dir.exist?(dir)
|
|
243
|
+
# :nocov:
|
|
244
|
+
|
|
245
|
+
Dir.glob(File.join(dir, "*.rb")).sort.each do |file|
|
|
246
|
+
require_relative file.sub(%r{^#{__dir__}/}, "")
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Register built-in types and behaviors
|
|
251
|
+
Synthra.register_builtin_types!
|
|
252
|
+
Synthra::Behaviors::Registry.register_builtins!
|
|
253
|
+
|
|
254
|
+
# Auto-load config file if present
|
|
255
|
+
# :nocov: — load-time, runs exactly once at require. Which arm is taken depends
|
|
256
|
+
# on whether a config file is present in the boot CWD; it cannot be re-driven by
|
|
257
|
+
# a unit test. (load_config_file! itself is covered directly in its own spec.)
|
|
258
|
+
Synthra.load_config_file! if Synthra::ConfigFile.find_config_file
|
|
259
|
+
# :nocov:
|
data/schemas/address.dsl
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Address schema example
|
|
2
|
+
Address:
|
|
3
|
+
street: text(10..100)
|
|
4
|
+
city: text(5..30)
|
|
5
|
+
state: text(2..20)
|
|
6
|
+
postal_code: postal_code(country: US)
|
|
7
|
+
country: country_code
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
PostalCode:
|
|
11
|
+
postal_code: text(5..10)
|
|
12
|
+
country: country_code
|
|
13
|
+
|
|
14
|
+
CountryCode:
|
|
15
|
+
country_code: text(2)
|
|
16
|
+
|
data/schemas/order.dsl
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Order schema with references
|
|
2
|
+
Order:
|
|
3
|
+
id: uuid(unique: true)
|
|
4
|
+
user_id: Ref(User.id)
|
|
5
|
+
status: enum(pending, processing:30%, shipped:40%, delivered:25%, cancelled:5%)
|
|
6
|
+
total: money(range: 10..1000, currency: USD)
|
|
7
|
+
items: array(OrderItem, 1..5)
|
|
8
|
+
created_at: past_date(30d)
|
|
9
|
+
shipped_at?: future_date(7d) if status
|
|
10
|
+
|
data/schemas/payment.dsl
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Payment schema
|
|
2
|
+
Payment:
|
|
3
|
+
id: uuid(unique: true)
|
|
4
|
+
order_id: Ref(Order.id)
|
|
5
|
+
amount: money
|
|
6
|
+
currency: currency
|
|
7
|
+
method: enum(credit_card:60%, debit_card:20%, paypal:15%, bank_transfer:5%)
|
|
8
|
+
status: enum(pending, completed:80%, failed:10%, refunded:10%)
|
|
9
|
+
processed_at?: timestamp
|
|
10
|
+
ip_address: ip
|
|
11
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Social media post schema
|
|
2
|
+
SocialPost:
|
|
3
|
+
id: ulid
|
|
4
|
+
author_id: uuid
|
|
5
|
+
content: text(10..280)
|
|
6
|
+
likes: number(0..10000)
|
|
7
|
+
shares: number(0..1000)
|
|
8
|
+
comments_count: number(0..500)
|
|
9
|
+
tags: array(text, 0..5)
|
|
10
|
+
media_url?: url
|
|
11
|
+
is_sponsored: boolean(true:10%)
|
|
12
|
+
created_at: past_date(30d)
|
|
13
|
+
|