tessa 1.2.3 → 2.0

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: 455d8debb812aaaada729eddbfd157c9214db5da5fe0906f1e5debaf9a2ca4c1
4
- data.tar.gz: 40c27986ec183944f8c5544caa54ebd2a1a041685627b9ff5556eaa87e99afd6
3
+ metadata.gz: 5d98c3c1a4dd8795f5631bee2260de0c0794f4eded2c71c6cf4bfaee23dc518e
4
+ data.tar.gz: 42c2a4748df92707676e06ecbb965e5245444bf343e468a5db60ea55a6b89337
5
5
  SHA512:
6
- metadata.gz: ba3575a6182a0bf896338ab41df520096f608fd8df8040a542629e94dffeaf8255f2b6e03a282eb637bfe201e28c57e8cc68f60977f65a4acf92e878b7b0e2d5
7
- data.tar.gz: 635386ff8364bfe1a5608651666009f9c07bb1aad05cf8a76f7bcacf0c422a2baa9432fad697c9907d919375ce55ab4f057b143f908a643c63a5de41de5cbed8
6
+ metadata.gz: bd8d14042de56677d144e26ff425551d5e7a68e95d331406e1556fbb34ec43f305203e139150fa05647091caaabe38c4d2df5cf29889d783665a8806a15e9ef8
7
+ data.tar.gz: d25ed80bf643b5ff4a126d67094b0ba66caf4f643f90efdfd4acec0dbbbf555bf1f60609bc51bc6bdb633cf5fb86feac3cde3d1cfb26adc4e75856cf76c55103
data/Gemfile CHANGED
@@ -11,4 +11,5 @@ end
11
11
  group :test do
12
12
  gem 'rspec-rails'
13
13
  gem 'webmock'
14
+ gem 'faraday'
14
15
  end
data/README.md CHANGED
@@ -1,11 +1,27 @@
1
- # [Tessa][0]
1
+ # [Tessa Client][0]
2
2
 
3
3
  [ ![Gem Version][1] ][2]
4
4
  [ ![Build Status][3] ][4]
5
5
  [ ![Coverage Status][5] ][6]
6
6
  [ ![Code Climate][7] ][8]
7
7
 
8
- Manage your assets.
8
+ This gem wraps ActiveStorage to provide some convenience helpers for direct uploading
9
+ assets, as well as generating public and private asset URLs.
10
+
11
+ ## History
12
+
13
+ This gem was initially conceived as a client to the
14
+ [Tessa asset management service](https://github.com/watermarkchurch/tessa). That service has been deprecated and is
15
+ being phased out.
16
+
17
+ In 2022, we moved away from using Tessa to manage our assets and got onto the official Rails ActiveStorage library.
18
+ [Here is the issue tracking the effort](https://github.com/watermarkchurch/tessa/issues/27). As part of that process,
19
+ we released the v1.x version of this gem which supports both ActiveStorage and Tessa but uploads all new assets to
20
+ ActiveStorage.
21
+
22
+ When that migration is completed, v2.x of this gem will remove all code that accesses the old Tessa service. This gem
23
+ will transition to being simply a convenience wrapper around ActiveStorage, so that we don't have to re-write as much
24
+ of our code in our frontend apps.
9
25
 
10
26
  ## Installation
11
27
 
@@ -25,7 +41,65 @@ Or install it yourself as:
25
41
 
26
42
  ## Usage
27
43
 
28
- TODO: Write usage instructions here
44
+ We provide no guarantee of support for your use of this gem. It is for internal Watermark use only.
45
+ **Use at your own risk!**
46
+
47
+ The main thing this gem still gives us is direct-uploads using [Dropzone.js](https://github.com/dropzone/dropzone).
48
+ Before using this gem it's worthwhile to understand how it works:
49
+
50
+ ![Sequence Diagram](./docs/tessa-activestorage-sequence-diagram.drawio.png)
51
+
52
+ When an Asset input is configured using the SimpleForm helper (TODO: https://github.com/watermarkchurch/tessa-client/issues/22),
53
+ it creates a DropZone div with the css class `.tessa-upload`. The javascript
54
+ in `app/javascript/tessa/index.js.coffee` scans for these divs and configures
55
+ Dropzone.js.
56
+
57
+ When a user initiates a file upload via Dropzone, it does an XHR POST to `/tessa/uploads`. This triggers
58
+ the `lib/tessa/rack_upload_proxy.rb` middleware to create a new ActiveStorage blob, and returns the
59
+ signed upload URL. Dropzone then uploads the file directly to S3 (or wherever ActiveStorage is configured)
60
+ from the user's browser.
61
+
62
+ To get all this working, follow these steps:
63
+
64
+ 1. Mount the engine
65
+ in your `config/routes.rb`, `mount Tessa::Engine, at: '/'`. It's important that it is mounted at root.
66
+
67
+ You can use Authentication around the engine to prevent unauthorized uploads. With devise it's as simple as:
68
+ ```rb
69
+ authenticated :user do
70
+ mount Tessa::Engine, at: '/'
71
+ end
72
+ ```
73
+
74
+ 2. In your application.js, require the js libraries:
75
+ ```js
76
+ //= require dropzone
77
+ //= require tessa
78
+ ```
79
+ Note that this only works if you are using Sprockets.
80
+ If you are using another bundler, we don't support that yet.
81
+
82
+ 3. In your model, use the Tessa `asset` declaration instead of `has_one_attached`. The SimpleForm helper only works
83
+ with Tessa `asset` declarations so far.
84
+
85
+ ```rb
86
+ class Model < ApplicationRecord
87
+ include Tessa::Model
88
+
89
+ asset :image # Note: this essentially delegates to has_one_attached
90
+ ```
91
+
92
+ 4. When rendering your form, use the SimpleForm helper to render the dropzone div:
93
+
94
+ ```erb
95
+ <%= f.input :image,
96
+ as: :asset,
97
+ dropzone: { acceptedFiles: "image/*" },
98
+ hint: "Use an image that is 1440 x 288 in size (5:1 aspect ratio)" %>
99
+ ```
100
+
101
+ 5. Configure your ActiveStorage service to accept direct uploads.
102
+ The disk service does this automatically. The S3 service requires additional CORS configuration.
29
103
 
30
104
  ## Contributing
31
105
 
data/lib/tasks/tessa.rake CHANGED
@@ -2,6 +2,17 @@
2
2
  namespace :tessa do
3
3
  desc "Begins the migration of all Tessa assets to ActiveStorage."
4
4
  task :migrate => :environment do
5
- Tessa::MigrateAssetsJob.perform_later
5
+ abort "Tessa::MigrateAssetsJob can no longer be performed because the Tessa connection was removed. "\
6
+ "Please downgrade to tessa ~>1.0 and try again."
7
+ end
8
+
9
+ desc "Verifies that the migration has completed"
10
+ task :verify => :environment do
11
+ unless Tessa::MigrateAssetsJob.complete?
12
+ state = Tessa::MigrateAssetsJob::ProcessingState.initialize_from_models
13
+
14
+ abort "Tessa::MigrateAssetsJob not yet complete! #{state.count} records remain to be migrated. "\
15
+ "Please downgrade to tessa ~>1.0 and try again."
16
+ end
6
17
  end
7
18
  end
data/lib/tessa/config.rb CHANGED
@@ -10,15 +10,7 @@ module Tessa
10
10
  attribute :strategy, String, default: -> (*_) { ENV['TESSA_STRATEGY'] || DEFAULT_STRATEGY }
11
11
 
12
12
  def connection
13
- @connection ||= Faraday.new(url: url) do |conn|
14
- if conn.respond_to?(:basic_auth)
15
- conn.basic_auth username, password
16
- else # Faraday >= 1.0
17
- conn.request :authorization, :basic, username, password
18
- end
19
- conn.request :url_encoded
20
- conn.adapter Faraday.default_adapter
21
- end
13
+ @connection ||= Tessa::FakeConnection.new
22
14
  end
23
15
  end
24
16
  end
@@ -0,0 +1,29 @@
1
+ module Tessa
2
+ # Since we no longer connect to the Tessa service, fake out the Tessa connection
3
+ # so that it always returns 503
4
+ class FakeConnection
5
+
6
+ [:get, :head, :put, :post, :patch, :delete].each do |method|
7
+ define_method(method) do |*args|
8
+ if defined?(Bugsnag)
9
+ Bugsnag.notify("Tessa::FakeConnection##{method} invoked")
10
+ end
11
+ Tessa::FakeConnection::Response.new()
12
+ end
13
+ end
14
+
15
+ class Response
16
+ def success?
17
+ false
18
+ end
19
+
20
+ def status
21
+ 503
22
+ end
23
+
24
+ def body
25
+ '{ "error": "The Tessa connection is no longer implemented." }'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -72,15 +72,6 @@ class Tessa::MigrateAssetsJob < ActiveJob::Base
72
72
  reupload(record, field_state)
73
73
  Rails.logger.info("#{record.class}#{record.id}##{field_state.field_name}: success")
74
74
  field_state.success_count += 1
75
- rescue OpenURI::HTTPError => ex
76
- if ex.message == "404 Not Found"
77
- # clear out the field if the asset is missing
78
- record.public_send("#{field_state.tessa_field.name}=", nil)
79
- record.save!
80
- else
81
- Rails.logger.error("#{record.class}#{record.id}##{field_state.field_name}: error - #{ex}")
82
- end
83
- field_state.offset += 1
84
75
  rescue StandardError => ex
85
76
  Rails.logger.error("#{record.class}#{record.id}##{field_state.field_name}: error - #{ex}")
86
77
  field_state.offset += 1
@@ -136,18 +127,24 @@ class Tessa::MigrateAssetsJob < ActiveJob::Base
136
127
  end
137
128
 
138
129
  def load_models_from_registry
139
- Rails.application.eager_load!
140
-
141
- # Load all Tessa models that can have attachments (not form objects)
142
- models = Tessa.model_registry
143
- .select { |m| m.respond_to?(:has_one_attached) }
144
-
145
130
  # Initialize our Record Keeping object
146
- ProcessingState.initialize_from_models(models)
131
+ ProcessingState.initialize_from_models
132
+ end
133
+
134
+ # Determines whether the migrate asset job is completed. If true, running the
135
+ # job again will not do anything.
136
+ def self.complete?
137
+ ProcessingState.initialize_from_models.fully_processed?
147
138
  end
148
139
 
149
140
  ProcessingState = Struct.new(:model_queue, :batch_count) do
150
- def self.initialize_from_models(models)
141
+ def self.initialize_from_models(models = nil)
142
+ unless models
143
+ # Load all Tessa models that can have attachments (not form objects)
144
+ Rails.application.eager_load!
145
+ models = Tessa.model_registry.select { |m| m.respond_to?(:has_one_attached) }
146
+ end
147
+
151
148
  new(
152
149
  models.map do |model|
153
150
  ModelProcessingState.initialize_from_model(model)
@@ -22,19 +22,11 @@ class Tessa::DynamicExtensions
22
22
  if #{name}_attachment.present?
23
23
  return Tessa::ActiveStorage::AssetWrapper.new(#{name}_attachment)
24
24
  end
25
-
26
- # fall back to old Tessa fetch if not present
27
- if field = self.class.tessa_fields["#{name}".to_sym]
28
- @#{name} ||= fetch_tessa_remote_assets(field.id(on: self))
29
- end
30
25
  end
31
26
 
32
27
  def #{field.id_field}
33
28
  # Use the attachment's key
34
29
  return #{name}_attachment.key if #{name}_attachment.present?
35
-
36
- # fallback to Tessa's database column
37
- super
38
30
  end
39
31
 
40
32
  def #{name}=(attachable)
@@ -80,7 +72,6 @@ class Tessa::DynamicExtensions
80
72
 
81
73
  @#{name} ||= [
82
74
  *#{name}_attachments.map { |a| Tessa::ActiveStorage::AssetWrapper.new(a) },
83
- *fetch_tessa_remote_assets(tessa_ids)
84
75
  ]
85
76
  end
86
77
 
@@ -88,8 +79,6 @@ class Tessa::DynamicExtensions
88
79
  [
89
80
  # Use the attachment's key
90
81
  *#{name}_attachments.map(&:key),
91
- # include from Tessa's database column
92
- *super
93
82
  ]
94
83
  end
95
84
 
@@ -105,8 +94,8 @@ class Tessa::DynamicExtensions
105
94
  existing.destroy
106
95
  else
107
96
  ids = self.#{field.id_field}
108
- ids.delete(change.id.to_i)
109
- self.#{field.id_field} = ids.any? ? ids : nil
97
+ ids&.delete(change.id.to_i)
98
+ self.#{field.id_field} = ids&.any? ? ids : nil
110
99
  end
111
100
  end
112
101
  attachables.changes.select(&:add?).each do |change|
@@ -138,10 +127,7 @@ class Tessa::DynamicExtensions
138
127
  def build(mod)
139
128
  mod.class_eval <<~CODE, __FILE__, __LINE__ + 1
140
129
  attr_accessor :#{name}_id
141
- attr_writer :#{name}
142
- def #{name}
143
- @#{name} ||= fetch_tessa_remote_assets(#{name}_id)
144
- end
130
+ attr_accessor :#{name}
145
131
  CODE
146
132
  mod
147
133
  end
@@ -151,12 +137,9 @@ class Tessa::DynamicExtensions
151
137
  def build(mod)
152
138
  mod.class_eval <<~CODE, __FILE__, __LINE__ + 1
153
139
  attr_accessor :#{name}_ids
154
- attr_writer :#{name}
155
- def #{name}
156
- @#{name} ||= fetch_tessa_remote_assets(#{name}_ids)
157
- end
140
+ attr_accessor :#{name}
158
141
  CODE
159
142
  mod
160
143
  end
161
144
  end
162
- end
145
+ end
data/lib/tessa/model.rb CHANGED
@@ -8,8 +8,6 @@ module Tessa
8
8
  def self.included(base)
9
9
  base.send :include, InstanceMethods
10
10
  base.extend ClassMethods
11
- base.after_commit :apply_tessa_change_sets if base.respond_to?(:after_commit)
12
- base.before_destroy :remove_all_tessa_assets if base.respond_to?(:before_destroy)
13
11
 
14
12
  Tessa.model_registry << base
15
13
  end
@@ -21,9 +19,9 @@ module Tessa
21
19
  end
22
20
 
23
21
  def apply_tessa_change_sets
24
- pending_tessa_change_sets.delete_if do |_, change_set|
25
- change_set.apply
26
- end
22
+ # Pretend like the application was successful but we didn't do anything
23
+ # because everything is in ActiveStorage now
24
+ pending_tessa_change_sets.clear
27
25
  end
28
26
 
29
27
  def remove_all_tessa_assets
@@ -37,17 +35,9 @@ module Tessa
37
35
  end
38
36
 
39
37
  def fetch_tessa_remote_assets(ids)
38
+ # This should just always return Tessa::AssetFailure
40
39
  Tessa.find_assets(ids)
41
40
  end
42
-
43
- private def reapplying_asset?(field, change_set)
44
- additions = change_set.changes.select(&:add?)
45
-
46
- return false if additions.none?
47
- return false if change_set.changes.size > additions.size
48
-
49
- additions.all? { |a| field.ids(on: self).include?(a.id) }
50
- end
51
41
  end
52
42
 
53
43
  module ClassMethods
@@ -13,8 +13,6 @@ class Tessa::Upload::UploadsFile
13
13
  end
14
14
 
15
15
  def self.connection_factory
16
- Faraday.new do |conn|
17
- conn.adapter Faraday.default_adapter
18
- end
16
+ Tessa::FakeConnection.new
19
17
  end
20
18
  end
data/lib/tessa/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tessa
2
- VERSION = "1.2.3"
2
+ VERSION = "2.0"
3
3
  end
data/lib/tessa.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  require "tessa/version"
2
2
 
3
- require "faraday"
4
3
  require "virtus"
5
4
  require "json"
6
5
 
6
+ require "tessa/fake_connection"
7
7
  require "tessa/config"
8
8
  require "tessa/response_factory"
9
9
  require "tessa/asset"
@@ -24,16 +24,11 @@ RSpec.shared_examples_for "remote call macro" do |method, path, return_type|
24
24
  end
25
25
 
26
26
  context "when response is not successful" do
27
- let(:faraday_stubs) {
28
- Faraday::Adapter::Test::Stubs.new do |stub|
29
- stub.send(method, path) { |env| [422, {}, { "error" => "error" }.to_json] }
30
- end
31
- }
27
+ let(:connection) { Tessa::FakeConnection.new }
32
28
 
33
29
  it "raises Tessa::RequestFailed" do
34
30
  expect{ call }.to raise_error { |error|
35
31
  expect(error).to be_a(Tessa::RequestFailed)
36
- expect(error.response).to be_a(Faraday::Response)
37
32
  }
38
33
  end
39
34
  end
@@ -67,46 +67,4 @@ RSpec.describe Tessa::Config do
67
67
  expect(cfg.strategy).to eq("my-new-value")
68
68
  end
69
69
  end
70
-
71
- describe "#connection" do
72
- it "is a Faraday::Connection" do
73
- expect(cfg.connection).to be_a(Faraday::Connection)
74
- end
75
-
76
- context "with values cfgured" do
77
- subject { cfg.connection }
78
- before { args.each { |k, v| cfg.send("#{k}=", v) } }
79
- let(:args) {
80
- {
81
- url: "http://tessa.test",
82
- username: "username",
83
- password: "password",
84
- }
85
- }
86
-
87
- it "sets faraday's url prefix to our url" do
88
- expect(subject.url_prefix.to_s).to match(cfg.url)
89
- end
90
-
91
- context "with faraday spy" do
92
- let(:spy) { instance_spy(Faraday::Connection) }
93
- before do
94
- expect(Faraday).to receive(:new).and_yield(spy)
95
- subject
96
- end
97
-
98
- it "sets up url_encoded request handler" do
99
- expect(spy).to have_received(:request).with(:url_encoded)
100
- end
101
-
102
- it "cfgures the default adapter" do
103
- expect(spy).to have_received(:adapter).with(:net_http)
104
- end
105
- end
106
- end
107
-
108
- it "caches the result" do
109
- expect(cfg.connection.object_id).to eq(cfg.connection.object_id)
110
- end
111
- end
112
70
  end
@@ -93,17 +93,10 @@ RSpec.describe Tessa::Model do
93
93
  let(:instance) { model.new(another_place: []) }
94
94
  subject(:getter) { instance.multiple_field }
95
95
 
96
- it "calls find for each of the file_ids and returns result" do
96
+ it "No longer calls Tessa::Asset#find" do
97
97
  instance.another_place = [1, 2, 3]
98
- expect(Tessa::Asset).to receive(:find).with([1, 2, 3]).and_return([:a1, :a2, :a3])
99
- expect(getter).to eq([:a1, :a2, :a3])
100
- end
101
-
102
- it "caches the result" do
103
- instance.another_place = [1]
104
- expect(Tessa::Asset).to receive(:find).and_return(:val).once
105
- instance.multiple_field
106
- instance.multiple_field
98
+ expect(Tessa::Asset).to_not receive(:find)
99
+ expect(getter).to eq([])
107
100
  end
108
101
 
109
102
  context "with no values" do
@@ -121,17 +114,10 @@ RSpec.describe Tessa::Model do
121
114
  }
122
115
  subject(:getter) { instance.avatar }
123
116
 
124
- it "calls find for file_id and returns result" do
125
- instance.avatar_id = 1
126
- expect(Tessa::Asset).to receive(:find).with(1).and_return(:a1)
127
- expect(getter).to eq(:a1)
128
- end
129
-
130
- it "caches the result" do
117
+ it "No longer calls Tessa::Asset#find" do
131
118
  instance.avatar_id = 1
132
- expect(Tessa::Asset).to receive(:find).and_return(:val).once
133
- instance.avatar
134
- instance.avatar
119
+ expect(Tessa::Asset).to_not receive(:find)
120
+ expect(getter).to eq(nil)
135
121
  end
136
122
 
137
123
  it "wraps ActiveStorage uploads with AssetWrapper" do
@@ -334,245 +320,6 @@ RSpec.describe Tessa::Model do
334
320
 
335
321
  expect(instance.another_place).to eq(keys)
336
322
  end
337
-
338
- it 'replaces Tessa assets with ActiveStorage assets' do
339
- # Before deploying this code, we previously had DB records with Tessa IDs
340
- instance.update!(another_place: [1, 2, 3])
341
-
342
- # In this HTTP POST, we removed one of the tessa assets and uploaded a
343
- # new ActiveStorage asset
344
- blob = ::ActiveStorage::Blob.create_before_direct_upload!({
345
- filename: 'README.md',
346
- byte_size: file.size,
347
- content_type: file.content_type,
348
- checksum: '1234'
349
- })
350
- changeset = Tessa::AssetChangeSet.new(
351
- changes: [
352
- { 'id' => 1, 'action' => 'add' },
353
- { 'id' => 2, 'action' => 'remove' },
354
- { 'id' => 3, 'action' => 'add' },
355
- { 'id' => blob.signed_id, 'action' => 'add' },
356
- ]
357
- )
358
-
359
- # We'll download these assets when we access #multiple_field
360
- allow(Tessa.config.connection).to receive(:get)
361
- .with("/assets/1,3")
362
- .and_return(double("response",
363
- success?: true,
364
- body: [
365
- { 'id' => 1, 'public_url' => 'test1' },
366
- { 'id' => 2, 'public_url' => 'test2' }
367
- ].to_json))
368
-
369
- blob.upload(file)
370
-
371
- # act
372
- instance.multiple_field = changeset
373
-
374
- expect(instance.another_place).to eq([
375
- blob.key, 1, 3
376
- ])
377
-
378
- assets = instance.multiple_field
379
- expect(assets[0].key).to eq(blob.key)
380
- expect(assets[0].service_url)
381
- .to start_with('https://www.example.com/rails/active_storage/disk/')
382
-
383
- expect(assets[1].id).to eq(1)
384
- expect(assets[1].public_url).to eq('test1')
385
- expect(assets[2].id).to eq(2)
386
- expect(assets[2].public_url).to eq('test2')
387
- end
388
- end
389
- end
390
-
391
- describe "#apply_tessa_change_sets" do
392
- let(:instance) { model.new }
393
- let(:sets) { [ instance_spy(Tessa::AssetChangeSet) ] }
394
-
395
- before do
396
- instance.instance_variable_set(
397
- :@pending_tessa_change_sets,
398
- {
399
- avatar: sets[0],
400
- }
401
- )
402
- end
403
-
404
- it "iterates over all pending changesets calling apply" do
405
- instance.apply_tessa_change_sets
406
- expect(sets[0]).to have_received(:apply)
407
- end
408
-
409
- it "removes all changesets from list" do
410
- instance.apply_tessa_change_sets
411
- expect(instance.pending_tessa_change_sets).to be_empty
412
- end
413
-
414
- context "no @pending_tessa_change_sets ivar" do
415
- before do
416
- instance.instance_variable_set(
417
- :@pending_tessa_change_sets,
418
- nil
419
- )
420
- end
421
-
422
- it "doesn't raise error" do
423
- expect { instance.apply_tessa_change_sets }.to_not raise_error
424
- end
425
323
  end
426
324
  end
427
-
428
- describe "#fetch_tessa_remote_assets" do
429
- subject(:result) { model.new.fetch_tessa_remote_assets(arg) }
430
-
431
- context "argument is `nil`" do
432
- let(:arg) { nil }
433
-
434
- it "returns nil" do
435
- expect(result).to be_nil
436
- end
437
- end
438
-
439
- context "argument is `[]`" do
440
- let(:arg) { [] }
441
-
442
- it "returns []" do
443
- expect(result).to be_a(Array)
444
- expect(result).to be_empty
445
- end
446
- end
447
-
448
- context "when argument is not blank" do
449
- let(:id) { rand(100) }
450
- let(:arg) { id }
451
-
452
- it "calls Tessa::Asset.find with arguments" do
453
- expect(Tessa::Asset).to receive(:find).with(arg)
454
- result
455
- end
456
-
457
- context "when Tessa::Asset.find raises RequestFailed exception" do
458
- let(:error) {
459
- Tessa::RequestFailed.new("test exception", double(status: '500'))
460
- }
461
-
462
- before do
463
- allow(Tessa::Asset).to receive(:find).and_raise(error)
464
- end
465
-
466
- context "argument is single id" do
467
- let(:arg) { id }
468
-
469
- it "returns Failure" do
470
- expect(result).to be_a(Tessa::Asset::Failure)
471
- end
472
-
473
- it "returns asset with proper data" do
474
- expect(result.id).to eq(arg)
475
- end
476
- end
477
-
478
- context "argument is array" do
479
- let(:arg) { [ id, id * 2 ] }
480
-
481
- it "returns array" do
482
- expect(result).to be_a(Array)
483
- end
484
-
485
- it "returns instances of Failure" do
486
- expect(result).to all( be_a(Tessa::Asset::Failure) )
487
- end
488
-
489
- it "returns array with an asset for each id passed" do
490
- arg.zip(result) do |a, r|
491
- expect(r.id).to eq(a)
492
- end
493
- end
494
- end
495
- end
496
- end
497
- end
498
-
499
- describe "#remove_all_tessa_assets" do
500
- let(:instance) { model.new }
501
-
502
- context "with a single typed field" do
503
- let(:model) {
504
- SingleAssetModel
505
- }
506
-
507
- before do
508
- instance.avatar_id = 1
509
- end
510
-
511
- it "adds pending change sets for each field removing all current assets" do
512
- instance.remove_all_tessa_assets
513
- changes = instance.pending_tessa_change_sets.values
514
- .reduce(Tessa::AssetChangeSet.new, :+)
515
- .changes
516
- .map { |change| [change.id, change.action.to_sym] }
517
- expect(changes).to eq([
518
- [1, :remove]
519
- ])
520
- end
521
- end
522
-
523
-
524
- context "with a multiple typed field" do
525
- let(:model) {
526
- MultipleAssetModel
527
- }
528
- let(:instance) { model.new(another_place: []) }
529
-
530
- before do
531
- instance.another_place = [2, 3]
532
- end
533
-
534
- it "adds pending change sets for each field removing all current assets" do
535
- instance.remove_all_tessa_assets
536
- changes = instance.pending_tessa_change_sets.values
537
- .reduce(Tessa::AssetChangeSet.new, :+)
538
- .changes
539
- .map { |change| [change.id, change.action.to_sym] }
540
- expect(changes).to eq([
541
- [2, :remove],
542
- [3, :remove],
543
- ])
544
- end
545
- end
546
- end
547
-
548
- describe "adds callbacks" do
549
- context "model responds to after_commit" do
550
- let(:model) {
551
- Class.new do
552
- def self.after_commit(arg=nil)
553
- @after_commit ||= arg
554
- end
555
- end.tap { |c| c.send(:include, described_module) }
556
- }
557
-
558
- it "calls it with :apply_tessa_change_sets" do
559
- expect(model.after_commit).to eq(:apply_tessa_change_sets)
560
- end
561
- end
562
-
563
- context "model responds to before_destroy" do
564
- let(:model) {
565
- Class.new do
566
- def self.before_destroy(arg=nil)
567
- @before_destroy ||= arg
568
- end
569
- end.tap { |c| c.send(:include, described_module) }
570
- }
571
-
572
- it "calls it with :remove_all_tessa_assets" do
573
- expect(model.before_destroy).to eq(:remove_all_tessa_assets)
574
- end
575
- end
576
- end
577
-
578
325
  end
@@ -0,0 +1,23 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe Tessa do
4
+
5
+ describe '#find_assets' do
6
+ it 'returns AssetFailure for singular asset' do
7
+ result = Tessa.find_assets(1)
8
+
9
+ expect(result).to be_a(Tessa::Asset::Failure)
10
+ expect(result.message).to eq("The service is unavailable at this time.")
11
+ end
12
+
13
+ it 'returns AssetFailure array for multiple assets' do
14
+ result = Tessa.find_assets([1, 2, 3])
15
+
16
+ expect(result.count).to eq(3)
17
+ result.each do |r|
18
+ expect(r).to be_a(Tessa::Asset::Failure)
19
+ expect(r.message).to eq("The service is unavailable at this time.")
20
+ end
21
+ end
22
+ end
23
+ end
data/tessa.gemspec CHANGED
@@ -23,7 +23,6 @@ Gem::Specification.new do |spec|
23
23
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.add_dependency "faraday"
27
26
  spec.add_dependency "virtus", "~>1.0.4"
28
27
 
29
28
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tessa
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.3
4
+ version: '2.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Powell
@@ -9,22 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-03-20 00:00:00.000000000 Z
12
+ date: 2023-02-17 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: faraday
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '0'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '0'
28
14
  - !ruby/object:Gem::Dependency
29
15
  name: virtus
30
16
  requirement: !ruby/object:Gem::Requirement
@@ -151,6 +137,7 @@ files:
151
137
  - app/javascript/tessa/index.js.coffee
152
138
  - bin/rspec
153
139
  - config/routes.rb
140
+ - docs/tessa-activestorage-sequence-diagram.drawio.png
154
141
  - lib/tasks/tessa.rake
155
142
  - lib/tessa.rb
156
143
  - lib/tessa/active_storage/asset_wrapper.rb
@@ -161,6 +148,7 @@ files:
161
148
  - lib/tessa/config.rb
162
149
  - lib/tessa/controller_helpers.rb
163
150
  - lib/tessa/engine.rb
151
+ - lib/tessa/fake_connection.rb
164
152
  - lib/tessa/jobs/migrate_assets_job.rb
165
153
  - lib/tessa/model.rb
166
154
  - lib/tessa/model/dynamic_extensions.rb
@@ -250,6 +238,7 @@ files:
250
238
  - spec/tessa/rack_upload_proxy_spec.rb
251
239
  - spec/tessa/upload/uploads_file_spec.rb
252
240
  - spec/tessa/upload_spec.rb
241
+ - spec/tessa_spec.rb
253
242
  - tessa.gemspec
254
243
  - yarn.lock
255
244
  homepage: https://github.com/watermarkchurch/tessa-client
@@ -351,3 +340,4 @@ test_files:
351
340
  - spec/tessa/rack_upload_proxy_spec.rb
352
341
  - spec/tessa/upload/uploads_file_spec.rb
353
342
  - spec/tessa/upload_spec.rb
343
+ - spec/tessa_spec.rb