trailblazer-pro 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f87a1d9c0c7d557676ed48a4c3bab753f618b2593e018bcf9aeca16560257a26
4
- data.tar.gz: 6a97c8a6ff18033972a4113e5d794ff399cb2f64dc81dc8df8a3f76361cffcfc
3
+ metadata.gz: 6abc91ad413fd17c14fb3a1c665b9c1cc73701fd7ad41f82ef973dd726102349
4
+ data.tar.gz: c5407c726b22a5f74462c7847c4e454176f0a3e1596215e5555c17415051261b
5
5
  SHA512:
6
- metadata.gz: 4a2dc896a3d018eb93620f22e8ea5e528e2f6c737ede31612bf6ecd0f55824c863a99b30de790579b9d768988c693af0bf5b90eb5647e8e76c9cabd354c58fad
7
- data.tar.gz: 30eb6636827c3070582f1b26a145c1b98a1d6fda95f19eba9565c6e95fe68492ab483eca897af37337b965fe8bf5899cd89d3fa4761ae80aa1c0919dc85ba7ff
6
+ metadata.gz: 3b2837a745201f9cd075a38fd0dcbb3601301f98ff5b37a60827981d0ea88bdcc4894399c9a30210bb9b857fe6e422d1ae209323faf4a5300e1a27917712ebdb
7
+ data.tar.gz: dce8f6060c62fcc657440d334397e1406c8ac9116a5bc9f78ff5b344423bf023adda62e28b8191a68b20cd5a0b867d86f7257a395d41807e351e9047e4060552
@@ -16,3 +16,5 @@ jobs:
16
16
  gem install bundler -v 2.2.11
17
17
  bundle install
18
18
  bundle exec rake
19
+ bundle exec rake test_1
20
+ bundle exec rake test_2
data/CHANGELOG.md CHANGED
@@ -1,4 +1,6 @@
1
- ## [Unreleased]
1
+ ## [0.0.2]
2
+
3
+ - First real release!
2
4
 
3
5
  ## [0.0.1]
4
6
 
data/Gemfile CHANGED
@@ -8,6 +8,12 @@ gemspec
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  gem "minitest", "~> 5.0"
11
-
12
- # gem "faraday"
11
+ # gem "trailblazer-developer", path: "../trailblazer-developer"
12
+ gem "faraday"
13
13
  # gem "multi_json"
14
+ # gem "trailblazer-macro", path: "../trailblazer-macro"
15
+ # gem "trailblazer-activity", path: "../trailblazer-activity"
16
+ # gem "trailblazer-activity", github: "trailblazer/trailblazer-activity"
17
+ # gem "trailblazer-developer", path: "../trailblazer-developer"
18
+ # gem "trailblazer-operation", path: "../trailblazer-operation"
19
+ # gem "trailblazer-operation"
data/README.md CHANGED
@@ -1,35 +1,36 @@
1
1
  # Trailblazer::Pro
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/trailblazer/pro`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ A library to communicate with the TRAILBLAZER PRO platform.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ * Push local traces to us, and analyze using the online debugger.
6
+ * Export diagrams from the PRO editor to use with the `trailblazer-workflow` gem.
6
7
 
7
8
  ## Installation
8
9
 
9
10
  Add this line to your application's Gemfile:
10
11
 
11
12
  ```ruby
12
- gem 'trailblazer-pro'
13
+ gem "trailblazer-pro"
13
14
  ```
14
15
 
15
- And then execute:
16
+ ## Rails support
16
17
 
17
- $ bundle install
18
+ Check out the [Rails support docs](https://trailblazer.to/2.1/docs/pro) of `trailblazer-pro-rails` if you want to start web-debugging right away.
18
19
 
19
- Or install it yourself as:
20
+ ## Trace
20
21
 
21
- $ gem install trailblazer-pro
22
+ Retrieve you API key from https://pro.trailblazer.to/settings.
23
+ It will be something like `tpka_f5c698e2_d1ac_48fa_b59f_70e9ab100604`.
22
24
 
23
- ## Usage
25
+ ## Internals
24
26
 
25
- TODO: Write usage instructions here
27
+ ### Testing
26
28
 
27
- ## Development
29
+ Either run against our hosted https://test-pro-rails-jwt.onrender.com TRB PRO host, or locally. This is currently set in `test/test_helper.rb`.
28
30
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
31
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+ * `test/no_extend` Tests an environment where no monkey-patches happened, and if, only on anonymous classes.
32
33
 
33
- ## Contributing
34
+ ### Notes
34
35
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/trailblazer-pro.
36
+ * With `endpoint`, monkey-patching might become obsolete, since we can inject {:present_options} transparently.
data/Rakefile CHANGED
@@ -6,7 +6,19 @@ require "rake/testtask"
6
6
  Rake::TestTask.new(:test) do |t|
7
7
  t.libs << "test"
8
8
  t.libs << "lib"
9
- t.test_files = FileList["test/**/*_test.rb"]
9
+ t.test_files = FileList["test/**/*_test.rb"] - FileList["test/global_extend/*"]
10
10
  end
11
11
 
12
12
  task default: :test
13
+
14
+ Rake::TestTask.new(:test_1) do |t|
15
+ t.libs << "test"
16
+ t.libs << "lib"
17
+ t.test_files = FileList["test/global_extend/activity_call_test.rb"]
18
+ end
19
+
20
+ Rake::TestTask.new(:test_2) do |t|
21
+ t.libs << "test"
22
+ t.libs << "lib"
23
+ t.test_files = FileList["test/global_extend/integration_test.rb"]
24
+ end
@@ -0,0 +1,20 @@
1
+ module Trailblazer
2
+ module Pro
3
+ module Call
4
+ module Activity
5
+ # This is the monkey-patch for {Activity.call}.
6
+ # Here we decide whether to use tracing, and what to render,
7
+ # or if we should bypass tracing.
8
+ def call(activity, ctx)
9
+ trace_strategy, present_options_merge = Pro::Session.trace_guards.(activity, ctx)
10
+
11
+ if trace_strategy
12
+ return trace_strategy.invoke(activity, [ctx, {}], present_options: present_options_merge)
13
+ else
14
+ return super
15
+ end
16
+ end
17
+ end # Activity
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,124 @@
1
+ module Trailblazer
2
+ module Pro
3
+ module Debugger
4
+ module_function
5
+
6
+ # Called in {Trace::Present.call} as {:render_method}.
7
+ def call(debugger_trace:, activity:, render_wtf: false, renderer:, **options)
8
+ trace_data = render_trace_data(debugger_trace, activity: activity, **options)
9
+
10
+ trace_envelope = {
11
+ fields: {
12
+ activity_name: {stringValue: activity},
13
+ trace: {stringValue: trace_data},
14
+ created_at: {timestampValue: DateTime.now}, # we're using local client time currently.
15
+ }
16
+ }
17
+
18
+ session, stored_trace_id, session_updated = push(trace_envelope, activity: activity, **options)
19
+
20
+ debugger_url = "https://ide.trailblazer.to/#{stored_trace_id}"
21
+ # output = "[TRB PRO] view trace (#{activity}) at #{debugger_url}"
22
+ # output = Developer::Wtf::Renderer::String.bold(output)
23
+ output = Developer::Wtf::Renderer::String.bold("[TRB PRO] view trace (#{activity}) at ")
24
+ output += debugger_url # DISCUSS: what do we want bold here?
25
+
26
+ if render_wtf
27
+ # TODO: take the color_map from outside caller.
28
+ wtf_output = Developer::Trace::Present.render(debugger_trace: debugger_trace, renderer: renderer, color_map: Developer::Wtf::Renderer::DEFAULT_COLOR_MAP) # , activity: activity
29
+
30
+ output = [wtf_output, output].join("\n")
31
+ end
32
+
33
+ returned_values = [session, stored_trace_id, debugger_url, trace_envelope, session_updated]
34
+
35
+ return output, returned_values
36
+ end
37
+
38
+ def render_trace_data(debugger_trace, activity:, **)
39
+ flat_tree_json = debugger_trace.to_a.collect do |debugger_node|
40
+
41
+ # TODO: do we even need to grab tw by path here?
42
+ introspect_nodes_node = OpenStruct.new(task: debugger_node.task)
43
+ tw_render = Developer::Render::TaskWrap.render_for(debugger_node.activity, introspect_nodes_node)
44
+
45
+ # This rendering code has deep knowledge of Trace/pro/v1 tracing interface.
46
+ {
47
+ id: debugger_node.id.to_s,
48
+ runtime_id: debugger_node.runtime_id,
49
+ level: debugger_node.level,
50
+ label: debugger_node.label,
51
+ ctx_snapshots: {
52
+ before: debugger_node.snapshot_before.data[:ctx_variable_changeset].collect { |name, hash, has_changed| [name, {version: hash.to_s, has_changed: !!has_changed}] },
53
+ after: debugger_node.snapshot_after ?
54
+
55
+ debugger_node.snapshot_after.data[:ctx_variable_changeset].collect { |name, hash, has_changed| [name, {version: hash.to_s, has_changed: !!has_changed}] } # FIXME: of course, this is horrible.
56
+ : [],
57
+ },
58
+
59
+ rendered_task_wrap: tw_render,
60
+ }
61
+ end
62
+
63
+ JSON.dump(
64
+ nodes: flat_tree_json,
65
+ variable_versions: debugger_trace.to_h[:variable_versions].to_h,
66
+ pro_version: Pro::VERSION.to_s,
67
+ )
68
+ end
69
+
70
+ class Push < Trailblazer::Activity::Railway
71
+ step :session_initialized?,
72
+ Output(:failure) => Path(track_color: :signin, connect_to: Track(:rebuild)) do # FIXME: move to after {valid?}
73
+ # Signin only consumes {:api_key} and friends and doesn't know about {:session}.
74
+ step Subprocess(Trailblazer::Pro::Trace::Signin),
75
+ In() => :session_to_args#,
76
+ # Out() => Trace::Signin::SESSION_VARIABLE_NAMES
77
+ end
78
+
79
+ step Trace.method(:valid?), In() => :session_to_args, Inject() => [:now],
80
+ Output(:failure) => Path(track_color: :refresh, connect_to: Track(:rebuild)) do
81
+ step Subprocess(Trailblazer::Pro::Trace::Refresh), In() => :session_to_args
82
+ end
83
+
84
+ step :rebuild_session, magnetic_to: :rebuild # TODO: assert that success/failure go to right Track.
85
+
86
+ step Subprocess(Trailblazer::Pro::Trace::Store),
87
+ In() => :session_to_args,
88
+ In() => [:data_to_store]
89
+
90
+ def session_initialized?(ctx, session:, **)
91
+ session.is_a?(Session)
92
+ end
93
+
94
+ def rebuild_session(ctx, session:, **)
95
+ session_params = ctx.to_h.slice(*Trace::Signin::SESSION_VARIABLE_NAMES)
96
+
97
+ session = Session.new(
98
+ **session.to_h, # old data
99
+ **session_params, # new input
100
+ )
101
+
102
+ ctx[:session] = session
103
+ ctx[:session_updated] = true
104
+ end
105
+
106
+ def session_to_args(ctx, session:, **)
107
+ session.to_h
108
+ end
109
+ end # Push
110
+
111
+ def push(trace_data, activity:, now: DateTime.now, **options)
112
+ # signal, (ctx, _) = Trailblazer::Developer.wtf?(Push, [{now: now, data_to_store: trace_data, **options}, {}])
113
+ _signal, (ctx, _) = Trailblazer::Activity.(Push, {now: now, data_to_store: trace_data, **options})
114
+ # signal, (ctx, _) = Push.invoke([{now: now, data_to_store: trace_data, **options}, {}])
115
+
116
+ session = ctx[:session]
117
+ stored_trace_id = ctx[:id]
118
+ session_updated = ctx[:session_updated]
119
+
120
+ return session, stored_trace_id, session_updated
121
+ end
122
+ end # Debugger
123
+ end
124
+ end
@@ -0,0 +1,17 @@
1
+ module Trailblazer
2
+ module Pro
3
+ module Operation
4
+ module WTF
5
+ # {Operation.WTF?} will always use web tracing and CLI.
6
+ def WTF?(options)
7
+ call_with_public_interface(
8
+ options,
9
+ {},
10
+ invoke_class: Trailblazer::Pro::Trace::Wtf,
11
+ present_options: {render_wtf: true}
12
+ )
13
+ end
14
+ end
15
+ end # Operation
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module Trailblazer
2
+ module Pro
3
+ module Operation
4
+ # @private This is experimental.
5
+ module Call
6
+ # This is the monkey-patch for {Operation.call}.
7
+ # Here we decide whether to use tracing, and what to render,
8
+ # or if we should bypass tracing.
9
+ def call_with_public_interface(options, flow_options, **circuit_options)
10
+ trace_strategy, present_options_merge = Pro::Session.trace_guards.(self, options)
11
+
12
+ if trace_strategy
13
+ # trace_strategy.invoke(activity, [ctx, {}], present_options: present_options_merge)
14
+
15
+ # local invoke_class is overridden by circuit_options
16
+ super(options, flow_options, invoke_class: trace_strategy, **circuit_options, present_options: present_options_merge)
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end # Operation
23
+ end
24
+ end
@@ -0,0 +1,82 @@
1
+ module Trailblazer
2
+ module Pro
3
+ # DISCUSS: do we want {id_token} here explicitely?
4
+ # Data structure to hold all data values necessary to keep an authenticated
5
+ # session with Firebase.
6
+ class Session < Struct.new(:expires_at, :jwt_token_exp, :id_token, :firebase_upload_url, :firestore_fields_template, :firebase_refresh_url, :firebase_signin_url, :refresh_token, :api_key, :trailblazer_pro_host, keyword_init: true)
7
+ singleton_class.attr_accessor :wtf_present_options
8
+ singleton_class.attr_accessor :session
9
+ singleton_class.attr_accessor :trace_guards
10
+
11
+ self.trace_guards = Trailblazer::Pro::Trace::Decision.new([]) # here we say "don't trace anything!"
12
+ # .trace_guards = Pro::Trace::Decision.new([->(*) { [Trace::Wtf, {}] }]) # always use Pro web/CLI tracing per default.
13
+
14
+ class Uninitialized < Struct.new(:api_key, :trailblazer_pro_host, keyword_init: true)
15
+ end
16
+
17
+ def self.serialize(session)
18
+ attributes = session.to_h
19
+ attributes = attributes.slice(*(attributes.keys - [:expires_at]))
20
+
21
+ JSON.dump(attributes)
22
+ end
23
+
24
+ def self.deserialize(json)
25
+ data = JSON.parse(json)
26
+
27
+ options = data.key?("jwt_token_exp") ? {expires_at: Trace.parse_exp(data["jwt_token_exp"])} : {}
28
+
29
+ data
30
+ .merge(options) # TODO: use representer
31
+ .collect { |k, v| [k.to_sym, v] }
32
+ .to_h
33
+ end
34
+ end
35
+
36
+ # TODO:
37
+ # pass session, e.g. from RAils/tmp
38
+ def self.initialize!(api_key:, id_token: nil, render_wtf: true, **options)
39
+ Session.wtf_present_options = {
40
+ render_method: Trailblazer::Pro::Debugger,
41
+ render_wtf: render_wtf,
42
+ # api_key: api_key,
43
+ # **options
44
+ }
45
+
46
+ if id_token
47
+ Session.session = Trailblazer::Pro::Session.new(api_key: api_key, id_token: id_token, **options)
48
+ else
49
+ Session.session = Trailblazer::Pro::Session::Uninitialized.new(api_key: api_key, **options)
50
+ end
51
+ end
52
+
53
+ def self.trace_operations!(operation_hash)
54
+ Trailblazer::Pro::Session.trace_guards = DSL.trace_operations(operation_hash)
55
+ end
56
+
57
+ module DSL
58
+ module_function
59
+
60
+ def trace_operations(operation_hash)
61
+ decisions =
62
+ if operation_hash == :all
63
+ Trailblazer::Operation.extend(Trailblazer::Pro::Operation::Call)
64
+
65
+ raise "implement me"
66
+ elsif operation_hash.is_a?(Hash)
67
+ operation_hash.collect do |operation, strategy| # DISCUSS: this can be easily made faster for runtime.
68
+ operation.extend(Trailblazer::Pro::Operation::Call) # only extend selected OPs.
69
+
70
+ strategy = Trailblazer::Pro::Trace::Wtf if strategy === true # defaulting.
71
+
72
+ ->(activity, *) { activity == operation ? [strategy, {}] : false }
73
+ end
74
+ else
75
+ []
76
+ end
77
+
78
+ Trace::Decision.new(decisions)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,15 @@
1
+ module Trailblazer::Pro
2
+ module Trace
3
+ # Used in Activity.call monkey-patch.
4
+ class Decision < Struct.new(:guards)
5
+ def call(activity, ctx) # DISCUSS: signature not stable, yet.
6
+ guards.each do |guard|
7
+ result = guard.(activity, ctx) and return result # DISCUSS: {ctx.to_hash}?
8
+ end
9
+
10
+ false
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,33 @@
1
+ module Trailblazer::Pro
2
+ module Trace
3
+ class Refresh < Trailblazer::Activity::Railway
4
+ step :refresh_id_token
5
+ step Trace.method(:parse_response)
6
+ step :extract_id_token
7
+ step :extract_refresh_token
8
+ step Trace.method(:parse_jwt_token)
9
+ step Trace.method(:parse_expires_at)
10
+
11
+ def refresh_id_token(ctx, http: Faraday, refresh_token:, firebase_refresh_url:, **)
12
+ ctx[:response] = http.post(
13
+ firebase_refresh_url,
14
+ {
15
+ refresh_token: refresh_token,
16
+ grant_type: "refresh_token"
17
+ }.to_json,
18
+ {'Content-Type'=>'application/json', "Accept": "application/json"}
19
+ )
20
+
21
+ ctx[:response].status == 200
22
+ end
23
+
24
+ def extract_id_token(ctx, parsed_response:, **)
25
+ ctx[:id_token] = parsed_response["id_token"]
26
+ end
27
+
28
+ def extract_refresh_token(ctx, parsed_response:, **)
29
+ ctx[:refresh_token] = parsed_response["refresh_token"]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,97 @@
1
+ module Trailblazer::Pro
2
+ module Trace
3
+ def self.parse_response(ctx, response:, **)
4
+ ctx[:parsed_response] = JSON.parse(response.body)
5
+ end
6
+
7
+ require "jwt"
8
+ require "date"
9
+ def self.parse_jwt_token(ctx, id_token:, **)
10
+ token, _ = JWT.decode(id_token, nil, false, algorithm: "RS256")
11
+
12
+ ctx[:jwt_token_exp] = token["exp"]
13
+ # ctx[:jwt_token] = token
14
+ end
15
+
16
+ def self.parse_expires_at(ctx, jwt_token_exp:, **)
17
+ ctx[:expires_at] = parse_exp(jwt_token_exp)
18
+ end
19
+
20
+ def self.parse_exp(exp)
21
+ DateTime.strptime(exp.to_s, "%s")
22
+ end
23
+
24
+ def self.valid?(ctx, now:, expires_at:, **)
25
+ # FIXME
26
+ puts "id_token expires at #{expires_at}, that is in #{((expires_at - now) * 24 * 60 * 60).to_i} seconds"
27
+
28
+ now < expires_at
29
+ end
30
+
31
+ class Signin < Trailblazer::Activity::Railway
32
+ step :request_custom_token
33
+ step Trace.method(:parse_response)
34
+ step :extract_custom_token
35
+ step :extract_data_for_firebase
36
+ step :request_id_token
37
+ step Trace.method(:parse_response), id: :parse_firebase_response
38
+ step :extract_id_token
39
+ step :extract_refresh_token
40
+ step Trace.method(:parse_jwt_token)
41
+ step Trace.method(:parse_expires_at)
42
+ # left ->(ctx, response:, **) { puts response.status } # FIXME: better error handling!
43
+
44
+ PRO_SIGNIN_PATH = "/api/v1/signin_with_api_key"
45
+
46
+ # DISCUSS: this is the "outgoing" contract, the variables we should store in {session_params}.
47
+ SESSION_VARIABLE_NAMES = [
48
+ #:custom_token,
49
+ :id_token, :refresh_token, :expires_at, :jwt_token_exp, :firebase_signin_url, :firebase_refresh_url, :firebase_upload_url, :firestore_fields_template
50
+ ]
51
+
52
+ def request_custom_token(ctx, http: Faraday, api_key:, trailblazer_pro_host: "https://pro.trailblazer.to", **) # DISCUSS: do we like the defaulting?
53
+ ctx[:response] = http.post(
54
+ "#{trailblazer_pro_host}#{PRO_SIGNIN_PATH}",
55
+ {
56
+ api_key: api_key,
57
+ }.to_json,
58
+ {'Content-Type'=>'application/json', "Accept": "application/json"}
59
+ )
60
+
61
+ ctx[:response].status == 200
62
+ end
63
+
64
+ def extract_custom_token(ctx, parsed_response:, **)
65
+ ctx[:custom_token] = parsed_response["custom_token"]
66
+ end
67
+
68
+ def extract_data_for_firebase(ctx, parsed_response:, **)
69
+ ctx[:firebase_signin_url] = parsed_response["firebase_signin_url"] or return
70
+ ctx[:firebase_refresh_url] = parsed_response["firebase_refresh_url"] or return
71
+ ctx[:firebase_upload_url] = parsed_response["firebase_upload_url"] or return # needed in {Trace::Store}.
72
+ ctx[:firestore_fields_template] = parsed_response["firebase_upload_data"] or return
73
+ end
74
+
75
+ def request_id_token(ctx, http: Faraday, firebase_signin_url:, custom_token:, **)
76
+ ctx[:response] = http.post(
77
+ firebase_signin_url,
78
+ {
79
+ token: custom_token,
80
+ returnSecureToken: true
81
+ }.to_json,
82
+ {'Content-Type'=>'application/json', "Accept": "application/json"}
83
+ )
84
+
85
+ ctx[:response].status == 200
86
+ end
87
+
88
+ def extract_id_token(ctx, parsed_response:, **)
89
+ ctx[:id_token] = parsed_response["idToken"]
90
+ end
91
+
92
+ def extract_refresh_token(ctx, parsed_response:, **)
93
+ ctx[:refresh_token] = parsed_response["refreshToken"]
94
+ end
95
+ end # Signin
96
+ end
97
+ end
@@ -0,0 +1,38 @@
1
+ module Trailblazer::Pro
2
+ module Trace
3
+ class Store < Trailblazer::Activity::Railway
4
+ step :upload
5
+ step :extract_id
6
+ fail :error
7
+
8
+ def upload(ctx, firebase_upload_url:, data_to_store:, id_token:, firestore_fields_template:, **)
9
+ fields = data_to_store[:fields].merge(firestore_fields_template)
10
+
11
+ json_to_store = JSON.dump(data_to_store.merge(fields: fields))#.to_json
12
+
13
+ # puts "@@@@@ DATA SIZE: #{json_to_store.size / 1024} kb"
14
+
15
+ ctx[:response] = Faraday.post(
16
+ firebase_upload_url,
17
+ json_to_store,
18
+ {'Content-Type'=>'application/json', "Accept": "application/json",
19
+ "Authorization": "Bearer #{id_token}"
20
+ }
21
+ )
22
+
23
+ ctx[:response].status == 200
24
+ end
25
+
26
+ def extract_id(ctx, response:, **)
27
+ parsed_response = JSON.parse(response.body)
28
+
29
+ ctx[:firestore_name] = parsed_response["name"]
30
+ ctx[:id] = ctx[:firestore_name].split("/").last
31
+ end
32
+
33
+ def error(ctx, response:, **)
34
+ puts response.inspect
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ module Trailblazer::Pro
2
+ module Trace
3
+ module Wtf
4
+ module_function
5
+ # DISCUSS: this is called inside the monkey-patch for Activity/Operation.()
6
+ # in {Pro::Call.call}.
7
+ def call(*args, present_options: {}, **options)
8
+ global_present_options = Session.wtf_present_options
9
+ raise "[Trailblazer] Please configure your PRO API key." if global_present_options.nil?
10
+
11
+ present_options =
12
+ global_present_options
13
+ .merge(present_options)
14
+ .merge(session: Session.session)
15
+
16
+ returned = Trailblazer::Developer::Wtf.invoke( # identical to {Developer.wtf?}.
17
+ *args,
18
+ present_options: present_options,
19
+ **options
20
+ )
21
+
22
+ (session, _trace_id, _debugger_url, _trace_envelope, session_updated) = returned[-1]
23
+
24
+ update_session!(session) if session_updated # DISCUSS: this is a hook for pro-rails, not a massive fan.
25
+
26
+ returned
27
+ end
28
+
29
+ class << self
30
+ alias invoke call
31
+ end
32
+
33
+ def update_session!(session)
34
+ Session.session = session
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  module Pro
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
@@ -1,10 +1,20 @@
1
- # frozen_string_literal: true
2
-
3
1
  require_relative "pro/version"
2
+ require "trailblazer/activity/dsl/linear"
3
+ require "faraday"
4
4
 
5
5
  module Trailblazer
6
6
  module Pro
7
- class Error < StandardError; end
8
7
  # Your code goes here...
9
8
  end
10
9
  end
10
+
11
+ require_relative "pro/trace/decision"
12
+ require_relative "pro/session"
13
+ require_relative "pro/trace/signin"
14
+ require_relative "pro/trace/refresh"
15
+ require_relative "pro/trace/store"
16
+ require_relative "pro/trace/wtf"
17
+ require_relative "pro/debugger"
18
+ require_relative "pro/call/activity"
19
+ require_relative "pro/operation/call"
20
+ require_relative "pro/operation/WTF"
@@ -0,0 +1 @@
1
+ require "trailblazer/pro"
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  require_relative "lib/trailblazer/pro/version"
4
2
 
5
3
  Gem::Specification.new do |spec|
@@ -8,8 +6,7 @@ Gem::Specification.new do |spec|
8
6
  spec.authors = ["Nick Sutterer"]
9
7
  spec.email = ["apotonick@gmail.com"]
10
8
 
11
- spec.summary = "Integrations for our BPMN editor."
12
- # spec.description = "TODO: Write a longer description or delete this line."
9
+ spec.summary = "Integration code for TRB PRO."
13
10
  spec.homepage = "https://trailblazer.to/2.1/docs/pro.html"
14
11
  spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
15
12
 
@@ -27,8 +24,13 @@ Gem::Specification.new do |spec|
27
24
  spec.require_paths = ["lib"]
28
25
 
29
26
  # Uncomment to register a new dependency of your gem
30
- # spec.add_dependency "example-gem", "~> 1.0"
27
+ spec.add_dependency "trailblazer-activity", ">= 0.16.1", "< 0.17.0"
28
+ # spec.add_dependency "trailblazer-activity-dsl-linear", "~> 1.2"
29
+ spec.add_dependency "trailblazer-developer", ">= 0.1.0", "< 0.2.0"
30
+ spec.add_dependency "jwt"
31
+ spec.add_dependency "faraday"
32
+ spec.add_development_dependency "trailblazer-operation"
31
33
 
32
- # For more information and examples about making a new gem, checkout our
33
- # guide at: https://bundler.io/guides/creating_gem.html
34
+ spec.add_development_dependency "minitest-line"
35
+ spec.add_development_dependency "rake"
34
36
  end
metadata CHANGED
@@ -1,15 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-pro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-02-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2023-08-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: trailblazer-activity
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.16.1
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: 0.17.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.16.1
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.17.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: trailblazer-developer
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 0.1.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: 0.2.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 0.1.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: jwt
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: faraday
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: trailblazer-operation
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: minitest-line
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rake
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
13
123
  description:
14
124
  email:
15
125
  - apotonick@gmail.com
@@ -25,9 +135,18 @@ files:
25
135
  - Rakefile
26
136
  - bin/console
27
137
  - bin/setup
138
+ - lib/trailblazer-pro.rb
28
139
  - lib/trailblazer/pro.rb
29
- - lib/trailblazer/pro/client.rb
30
- - lib/trailblazer/pro/generate.rb
140
+ - lib/trailblazer/pro/call/activity.rb
141
+ - lib/trailblazer/pro/debugger.rb
142
+ - lib/trailblazer/pro/operation/WTF.rb
143
+ - lib/trailblazer/pro/operation/call.rb
144
+ - lib/trailblazer/pro/session.rb
145
+ - lib/trailblazer/pro/trace/decision.rb
146
+ - lib/trailblazer/pro/trace/refresh.rb
147
+ - lib/trailblazer/pro/trace/signin.rb
148
+ - lib/trailblazer/pro/trace/store.rb
149
+ - lib/trailblazer/pro/trace/wtf.rb
31
150
  - lib/trailblazer/pro/version.rb
32
151
  - trailblazer-pro.gemspec
33
152
  homepage: https://trailblazer.to/2.1/docs/pro.html
@@ -54,5 +173,5 @@ requirements: []
54
173
  rubygems_version: 3.2.3
55
174
  signing_key:
56
175
  specification_version: 4
57
- summary: Integrations for our BPMN editor.
176
+ summary: Integration code for TRB PRO.
58
177
  test_files: []
@@ -1,95 +0,0 @@
1
- require "faraday"
2
- require "base64"
3
- require "json"
4
- require "representable/json"
5
-
6
- module Trailblazer::Developer
7
- module Client
8
- Diagram = Struct.new(:id, :body)
9
-
10
- class Diagram::Representer < Representable::Decorator
11
- include Representable::JSON
12
- property :id
13
- property :body, as: :diagram
14
- end
15
-
16
- module_function
17
-
18
- def Diagram(id, body)
19
- Diagram.new(id, body).freeze
20
- end
21
-
22
- def import(id:, query:"", **options)
23
- token = retrieve_token(**options)
24
- export_diagram(id: id, token: token, query: query, **options)
25
- end
26
-
27
- def retrieve_token(email:, api_key:, url: "/signin", **options)
28
- body = JSON.generate({email: email, api_key: api_key})
29
-
30
- response = request(token: nil, method: :get, url: url, body: body, **options)
31
- return false unless response.status == 200
32
-
33
- # token = CGI::Cookie.parse(response.headers["set-cookie"])["token"][0]
34
- JSON.parse(response.body)["token"]
35
- end
36
-
37
- def export_diagram(id:, query:, **options)
38
- response = request(body: nil, url: "/api/v1/diagrams/#{id}/export#{query}", method: :get, **options)
39
-
40
- # parse_response(response)
41
- response.body
42
- end
43
-
44
- def duplicate(id:, **options)
45
- token = retrieve_token(**options)
46
-
47
- response = request(body: nil, token: token, url: "/api/v1/diagrams/#{id}/duplicate", method: :get, **options)
48
- parse_response(response)
49
- end
50
-
51
- # DISCUSS: do we need that?
52
- def new_diagram(token:, **options)
53
- response = request(body: nil, url: "/api/v1/diagrams/new", method: :get, token: token, **options)
54
-
55
- # TODO: use Dry::Struct
56
- # TODO: handle unauthorized/errors
57
- parse_response(response)
58
- end
59
-
60
- def request(host:, url:, method:, token:, body:, **)
61
- conn = Faraday.new(url: host)
62
-
63
- conn.send(method) do |req|
64
- req.url url
65
- req.headers["Content-Type"] = "application/json"
66
- req.body = body
67
- req.headers["Authorization"] = token
68
- end
69
- end
70
-
71
- def parse_response(response)
72
- diagram = Diagram.new
73
- Diagram::Representer.new(diagram).from_json(response.body) # a parsed hash would be cooler?
74
-
75
- diagram
76
- end
77
-
78
- # TODO: remove me!
79
- def self.push(operation:, name:)
80
- xml = Trailblazer::Diagram::BPMN.to_xml(operation["__activity__"], operation["__sequence__"].map(&:id))
81
- token = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6MywidXNlcm5hbWUiOiJhcG90b25pY2siLCJlbWFpbCI6Im5pY2tAdHJhaWxibGF6ZXIudG8ifQ." # rubocop:disable Metrics/LineLength
82
- conn = Faraday.new(url: "https://api.trb.to")
83
- response = conn.post do |req|
84
- req.url "/dev/v1/import"
85
- req.headers["Content-Type"] = "application/json"
86
- req.headers["Authorization"] = token
87
- require "base64"
88
-
89
- req.body = %({ "name": "#{name}", "xml":"#{Base64.strict_encode64(xml)}" })
90
- end
91
-
92
- puts response.status.inspect
93
- end
94
- end
95
- end
@@ -1,99 +0,0 @@
1
- gem "representable"
2
- require "representable/hash"
3
- require "trailblazer/activity/dsl/linear" # Railway.
4
-
5
- module Trailblazer
6
- module Developer
7
- # Computes an {Intermediate} data structure from a TRB-editor.js file.
8
- module Generate
9
- module_function
10
-
11
- Element = Struct.new(:id, :type, :linksTo, :data, :label, :parent)
12
- Arrow = Struct.new(:target, :label, :message, :target_lane)
13
-
14
- module Representer
15
- class Activity < Representable::Decorator
16
- include Representable::Hash
17
-
18
- collection :elements, class: Element do
19
- property :id
20
- property :type
21
- collection :linksTo, class: Arrow, default: ::Declarative::Variables::Append([]) do
22
- property :target
23
- property :label
24
- property :message
25
- property :target_lane
26
- end
27
- property :data, default: {}
28
-
29
- property :label
30
- property :parent # TODO: remove?
31
- end
32
- end
33
- end
34
-
35
- def call(hash)
36
- _, (ctx, _) = Activity::TaskWrap.invoke(Pipeline, [{hash: hash}, {}])
37
- ctx[:intermediate]
38
- end
39
-
40
- def transform_from_hash(ctx, hash:, parser: Representer::Activity, **)
41
- ctx[:elements] = parser.new(OpenStruct.new).from_hash(hash).elements
42
- end
43
-
44
- def find_start_events(ctx, elements:, **)
45
- ctx[:start_events] = elements.find_all { |el| el.type == "Event" }
46
- end
47
-
48
- def compute_intermediate(ctx, elements:, start_events:, **)
49
- end_events = elements.find_all { |el| el.type == "EndEventTerminate" } # DISCUSS: is it really called TERMINATE?
50
-
51
- inter = Activity::Schema::Intermediate
52
-
53
- wiring = elements.collect { |el|
54
- data = data_for(el)
55
-
56
- [inter.TaskRef(el.id, data), el.linksTo.collect { |arrow| inter.Out(semantic_for(**arrow.to_h), arrow.target) } ] }
57
- wiring = Hash[wiring]
58
-
59
- # end events need this stupid special handling
60
- # DISCUSS: currently, the END-SEMANTIC is read from the event's label.
61
- wiring = wiring.merge(Hash[
62
- end_events.collect do |_end|
63
- ref, = wiring.find { |ref, _| ref.id == _end.id }
64
-
65
- [ref, [inter.Out(semantic_for(**_end.to_h)|| raise, nil)]] # TODO: test the raise, happens when the semantic of an End can't be distinguished. # TODO: don't extract semantic from :label but from :data.
66
- end
67
- ])
68
- # pp wiring
69
-
70
- ctx[:intermediate] = inter.new(wiring, end_events.collect(&:id), start_events.collect(&:id))
71
- end
72
-
73
- # private
74
-
75
- def data_for(element)
76
- {type: element.type}.merge(element.data)
77
- end
78
-
79
- # We currently use the {:label} field of an arrow to encode an output semantic.
80
- # The {:symbol_style} part will be filtered out as semantic. Defaults to {:success}.
81
- def semantic_for(label:nil, **)
82
- return :success unless label
83
-
84
- extract_semantic(label)
85
- end
86
-
87
- def extract_semantic(label)
88
- label.to_sym
89
- end
90
-
91
- class Pipeline < Trailblazer::Activity::Railway
92
- step Generate.method(:transform_from_hash), id: :transform_from_hash
93
- step Generate.method(:find_start_events), id: :find_start_events
94
- step Generate.method(:compute_intermediate), id: :compute_intermediate
95
- end
96
- end
97
- end
98
- end
99
- # [Inter::Out(:success, nil)]