nexo 0.1.4 → 0.1.6

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -0
  3. data/app/controllers/nexo/element_versions_controller.rb +28 -0
  4. data/app/controllers/nexo/elements_controller.rb +64 -0
  5. data/app/controllers/nexo/folders_controller.rb +41 -0
  6. data/app/controllers/nexo/nexo_controller.rb +5 -0
  7. data/app/jobs/nexo/api_clients.rb +20 -18
  8. data/app/jobs/nexo/base_job.rb +5 -3
  9. data/app/jobs/nexo/delete_remote_resource_job.rb +11 -1
  10. data/app/jobs/nexo/fetch_remote_resource_job.rb +61 -0
  11. data/app/jobs/nexo/folder_check_status_job.rb +10 -0
  12. data/app/jobs/nexo/folder_destroy_job.rb +2 -1
  13. data/app/jobs/nexo/folder_download_job.rb +15 -0
  14. data/app/jobs/nexo/folder_sync_job.rb +6 -0
  15. data/app/jobs/nexo/synchronizable_changed_job.rb +17 -2
  16. data/app/jobs/nexo/update_remote_resource_job.rb +58 -24
  17. data/app/lib/nexo/active_record_google_token_store.rb +3 -3
  18. data/app/lib/nexo/api_client/google_auth_service.rb +11 -6
  19. data/app/lib/nexo/api_client/google_calendar_service.rb +167 -32
  20. data/app/lib/nexo/api_client/google_calendar_sync_service.rb +84 -0
  21. data/app/lib/nexo/element_service.rb +236 -0
  22. data/app/lib/nexo/errors.rb +6 -4
  23. data/app/lib/nexo/event_receiver.rb +4 -0
  24. data/app/lib/nexo/folder_service.rb +55 -28
  25. data/app/lib/nexo/import_remote_element_version.rb +75 -0
  26. data/app/lib/nexo/policy_service.rb +27 -8
  27. data/app/models/concerns/nexo/calendar_event.rb +33 -2
  28. data/app/models/concerns/nexo/synchronizable.rb +25 -9
  29. data/app/models/nexo/client.rb +9 -10
  30. data/app/models/nexo/element.rb +22 -39
  31. data/app/models/nexo/element_version.rb +12 -2
  32. data/app/models/nexo/folder.rb +29 -17
  33. data/app/models/nexo/integration.rb +4 -3
  34. data/app/models/nexo/token.rb +6 -4
  35. data/app/views/layouts/nexo.html.erb +42 -0
  36. data/app/views/nexo/element_versions/show.html.erb +16 -0
  37. data/app/views/nexo/elements/index.html.erb +32 -0
  38. data/app/views/nexo/elements/show.html.erb +56 -0
  39. data/app/views/nexo/folders/index.html.erb +22 -0
  40. data/app/views/nexo/folders/show.html.erb +22 -0
  41. data/config/environment.rb +1 -0
  42. data/config/routes.rb +25 -0
  43. data/config/spring.rb +1 -0
  44. data/db/migrate/20250505192315_create_nexo_clients.rb +1 -1
  45. data/db/migrate/20250506125057_create_nexo_tokens.rb +1 -1
  46. data/db/migrate/20250512025950_create_nexo_elements.rb +2 -2
  47. data/db/migrate/20250604124821_element_sync_status.rb +11 -0
  48. data/db/migrate/20250612002919_google_sync_tokens.rb +5 -0
  49. data/db/migrate/20250623132502_folder_sync_direction.rb +8 -0
  50. data/db/migrate/20250718012839_synchronizable_nullable.rb +6 -0
  51. data/db/seeds.rb +5 -4
  52. data/lib/nexo/engine.rb +22 -5
  53. data/lib/nexo/version.rb +1 -1
  54. metadata +38 -4
  55. data/app/jobs/nexo/sync_element_job.rb +0 -93
  56. data/app/models/concerns/nexo/folder_policy.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8e9cc07511456093aa96d53df983acf4bfc770ba3de05df5bacfb683f80840ca
4
- data.tar.gz: 5b1918e753e917ac5e0331aa5d49fe03a5d0fcb87d64fc36bab85af638b79246
3
+ metadata.gz: aca3e3e57c524de464408274ab6309fbea580931420c9da736556d7997ed6bd6
4
+ data.tar.gz: 770c4033d4dc280ad32471800e59a6d6d5d5af7a150897d52e5fa69de043b393
5
5
  SHA512:
6
- metadata.gz: 973db6311a60eb0534b3ca072976190bb3b5be953bc3b8f6cbb6e39207ba127b54711dd5ec0adbcc3f2c5cc4c4272302ca558ad0572c687f6864402e7bec4bde
7
- data.tar.gz: 79ba89ed8a6dbbd17bdf8c6c055df802fb9ec4cca686075d2f5e2653c776d1b38599371158bd58fb3779aef6dffd8718bd9ea8b5c42e2403a30d917a5aa72505
6
+ metadata.gz: 299fe496fcaa3041699cf37fa39e6d897005a4d7a5343c912694ff72cec18a91d19d317c91898c6cd1d55aeba606967298f1ce73ea40008cc14efa4acd62d807
7
+ data.tar.gz: 63057165522974406b55ec157a46e1c664d17b81e341e7dd04a551e93266e94bc38a0501297d3151f4a0636ad7d8c01e1cd2f739a7bc37cc340f37f53b435d8b
data/README.md CHANGED
@@ -26,6 +26,17 @@ Configure good_job
26
26
  max_threads:
27
27
  queues:
28
28
 
29
+ ## Unhandled cases / TODO's
30
+
31
+ - Restore locally discarded Events. It creates a new Google Calendar event?
32
+
33
+ - Recurring google events
34
+
35
+ - IMPORTANTE: loggear las exceptions de los jobs
36
+
37
+ - Llevar mucho control de los jobs y las exceptions que puedan surgir como
38
+ ActiveRecord::PreparedStatementCacheExpired
39
+
29
40
  ## Contributing
30
41
  Contribution directions go here.
31
42
 
@@ -0,0 +1,28 @@
1
+ # :nocov: TODO
2
+ module Nexo
3
+ class ElementVersionsController < NexoController
4
+ def show
5
+ @element_version = ElementVersion.find(params[:id])
6
+ end
7
+
8
+ def sync
9
+ @element_version = ElementVersion.find(params[:id])
10
+
11
+ case params[:operation]
12
+ when "import"
13
+ ImportRemoteElementVersion.new.perform(@element_version)
14
+ notice = "Imported"
15
+ when "update_remote"
16
+ UpdateRemoteResourceJob.perform_later(@element_version)
17
+ notice = "enqueued UpdateRemoteResourceJob"
18
+ else
19
+ raise "unkown action"
20
+ end
21
+
22
+ redirect_to @element_version, notice:
23
+ rescue StandardError => e
24
+ redirect_to @element_version, alert: e.message
25
+ end
26
+ end
27
+ end
28
+ # :nocov:
@@ -0,0 +1,64 @@
1
+ # :nocov: TODO
2
+ module Nexo
3
+ class ElementsController < NexoController
4
+ before_action except: :index do
5
+ @element = Element.find(params[:id])
6
+ end
7
+
8
+ def index
9
+ @elements =
10
+ Element.includes(:synchronizable).order(id: :desc)
11
+ .page(params[:page]).per(params[:page_size] || 10)
12
+
13
+ if params[:not_synced]
14
+ @elements = @elements.where.not(ne_status: :synced)
15
+ end
16
+ if params[:without_synchronizable]
17
+ @elements = @elements.where(synchronizable_id: nil)
18
+ end
19
+ I18n.locale = :en
20
+ end
21
+
22
+ def show
23
+ end
24
+
25
+ def update_status
26
+ ElementService.new(element: @element).update_ne_status!
27
+
28
+ redirect_to @element, notice: "Updated status"
29
+ end
30
+
31
+ def modify_local
32
+ unless Rails.env.local?
33
+ redirect_to @element, alert: "Available only on local env"
34
+ return
35
+ end
36
+
37
+ event = @element.synchronizable
38
+ event.date_from = event.date_from + 1.day
39
+ event.date_to = event.date_to + 1.day
40
+ event.save!
41
+
42
+ EventReceiver.new.synchronizable_updated(event)
43
+
44
+ redirect_to @element, notice: "Modified"
45
+ rescue StandardError => e
46
+ redirect_to @element, alert: e.message
47
+ end
48
+
49
+ def fetch_remote
50
+ FetchRemoteResourceJob.perform_later(@element)
51
+
52
+ redirect_to @element, notice: "Enqueued FetchRemoteResourceJob"
53
+ end
54
+
55
+ def resolve_conflict
56
+ ElementService.new(element: @element).resolve_conflict!
57
+
58
+ redirect_to @element, notice: "Conflict solved"
59
+ rescue StandardError => e
60
+ redirect_to @element, alert: e.message
61
+ end
62
+ end
63
+ end
64
+ # :nocov:
@@ -0,0 +1,41 @@
1
+ # :nocov: TODO
2
+ module Nexo
3
+ class FoldersController < NexoController
4
+ before_action except: :index do
5
+ @folder = Folder.find(params[:id])
6
+ end
7
+
8
+ def index
9
+ page = params[:page].to_i || 0
10
+ page_size = 100
11
+ @folders = Folder.offset(page * page_size).limit(page_size).order(id: :desc)
12
+ end
13
+
14
+ def show
15
+ end
16
+
17
+ def check_status
18
+ FolderCheckStatusJob.perform_later(@folder)
19
+
20
+ redirect_to @folder, notice: "Checking status"
21
+ end
22
+
23
+ def sync
24
+ FolderDownloadJob.perform_later(@folder, "full_or_incremental_sync")
25
+
26
+ redirect_to @folder, notice: "Enqueued sync"
27
+ end
28
+
29
+ def full_sync
30
+ FolderDownloadJob.perform_later(@folder, "full_sync")
31
+
32
+ redirect_to @folder, notice: "Enqueued full sync"
33
+ end
34
+
35
+ def incremental_sync
36
+ FolderDownloadJob.perform_later(@folder, "incremental_sync")
37
+
38
+ redirect_to @folder, notice: "Enqueued incremental sync"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ module Nexo
2
+ class NexoController < ApplicationController
3
+ layout "nexo"
4
+ end
5
+ end
@@ -3,29 +3,31 @@ module Nexo
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
- include GoodJob::ActiveJobExtensions::Concurrency
6
+ queue_as :nexo_api_clients
7
7
 
8
- queue_as :api_clients
8
+ # :nocov: tricky
9
+ if defined? GoodJob
10
+ include GoodJob::ActiveJobExtensions::Concurrency
9
11
 
10
- # TODO!: configure good job queues and thread pools
11
- # https://github.com/bensheldon/good_job?tab=readme-ov-file#optimize-queues-threads-and-processes
12
- # TODO: make this configurable, so other job backends are allowed
13
- good_job_control_concurrency_with(
14
- perform_limit: 1,
12
+ # https://github.com/bensheldon/good_job?tab=readme-ov-file#optimize-queues-threads-and-processes
13
+ good_job_control_concurrency_with(
14
+ perform_limit: 1,
15
15
 
16
- perform_throttle: (Nexo.api_jobs_throttle || [ 100, 5.minute ]),
16
+ perform_throttle: (Nexo.api_jobs_throttle || [ 100, 5.minute ]),
17
17
 
18
- key: -> { "#{queue_name}" }
19
- )
18
+ key: -> { "#{queue_name}" }
19
+ )
20
20
 
21
- retry_on(
22
- GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError,
23
- attempts: Float::INFINITY,
24
- wait: :polynomially_longer,
25
- jitter: 0.99,
26
- # wait: ->(executions) { ((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2 }
27
- # wait: ->(executions) { ((executions**3) + (Kernel.rand * (executions**3) * 0.5)) + 2 }
28
- )
21
+ retry_on(
22
+ GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError,
23
+ attempts: Float::INFINITY,
24
+ wait: :polynomially_longer,
25
+ jitter: 0.99,
26
+ # wait: ->(executions) { ((executions**4) + (Kernel.rand * (executions**4) * jitter)) + 2 }
27
+ # wait: ->(executions) { ((executions**3) + (Kernel.rand * (executions**3) * 0.5)) + 2 }
28
+ )
29
+ end
30
+ # :nocov:
29
31
  end
30
32
  end
31
33
  end
@@ -1,8 +1,10 @@
1
1
  module Nexo
2
2
  class BaseJob < ActiveJob::Base
3
- def self.limits_concurrency(*)
4
- # TODO: implementar
5
- Rails.logger.error "ERROR: Implementar limits_concurrency"
3
+ # Like ActiveJob does with Rails.logger, we do with Nexo.logger
4
+ # push job's name and id tags
5
+ around_perform do |job, block|
6
+ tags = job.class.name, job.job_id
7
+ Nexo.plain_logger.tagged(*tags, &block)
6
8
  end
7
9
  end
8
10
  end
@@ -1,10 +1,20 @@
1
1
  module Nexo
2
+ # - Removing/discarding Element's
2
3
  class DeleteRemoteResourceJob < BaseJob
3
4
  include ApiClients
4
5
 
5
6
  def perform(element)
7
+ if element.discarded?
8
+ raise "element already discarded"
9
+ end
10
+
11
+ unless element.flagged_for_removal?
12
+ raise "element not flagged for removal"
13
+ end
14
+
6
15
  ServiceBuilder.instance.build_protocol_service(element.folder).remove(element)
7
- # TODO!: mark as deleted, or something
16
+
17
+ ElementService.new(element:).discard!
8
18
  end
9
19
  end
10
20
  end
@@ -0,0 +1,61 @@
1
+ module Nexo
2
+ # Always must be executed asynchronously to ensure the concurrency limit applies
3
+ #
4
+ # Responsabilities:
5
+ # - Creating a ElementVersion on external incoming change
6
+ # - Updating a Synchronizable for that external change
7
+ #
8
+ # @raise ActiveRecord::RecordNotUnique
9
+ class FetchRemoteResourceJob < BaseJob
10
+ include ApiClients
11
+
12
+ attr_reader :element
13
+
14
+ def perform(element)
15
+ @element = element
16
+
17
+ remote_service = ServiceBuilder.instance.build_protocol_service(element.folder)
18
+
19
+ response = remote_service.get_event(element)
20
+ # TODO!: handle calendar change
21
+
22
+ handle_response(element, response)
23
+ end
24
+
25
+ def handle_response(element, response)
26
+ # TODO!: refactor y que no tenga que repetir esta asignación
27
+ # extraer a algún Service?
28
+ @element = element
29
+
30
+ if response.present? && element.element_versions.where(etag: response.etag).empty?
31
+ Nexo.logger.debug { "Fetched new element version from remote server" }
32
+ Nexo.logger.debug { response.payload }
33
+
34
+ element_version = save_element_version(response)
35
+
36
+ if element.folder.sync_external_changes?
37
+ ImportRemoteElementVersion.new.perform(element_version)
38
+ else
39
+ Nexo.logger.info("Element version ignored_by_sync_direction")
40
+ end
41
+ else
42
+ Nexo.logger.debug { "No new version fetched from remote server" }
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def save_element_version(service_response)
49
+ nev_status =
50
+ element.folder.sync_external_changes? ? :pending_sync : :ignored_by_sync_direction
51
+
52
+ ElementService.new(element:).create_element_version!(
53
+ origin: :external,
54
+ etag: service_response.etag,
55
+ payload: service_response.payload,
56
+ sequence: nil,
57
+ nev_status:
58
+ )
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ module Nexo
2
+ class FolderCheckStatusJob < BaseJob
3
+ def perform(folder)
4
+ calendar = GoogleCalendarService.new(folder.integration).get_calendar(folder)
5
+ folder.update(nf_status: :ok)
6
+ rescue StandardError => e
7
+ folder.update(nf_status: :not_found)
8
+ end
9
+ end
10
+ end
@@ -1,11 +1,12 @@
1
1
  module Nexo
2
2
  class FolderDestroyJob < BaseJob
3
+ # TODO!: limit concurrency
3
4
  def perform(folder)
4
5
  if folder.external_identifier.present?
5
6
  protocol_service = ServiceBuilder.instance.build_protocol_service(folder)
6
7
  response = protocol_service.remove_calendar(folder)
7
8
  else
8
- Rails.logger.info("Folder doesn't have external_identifier: #{folder.to_gid}")
9
+ Nexo.logger.info("Folder doesn't have external_identifier: #{folder.to_gid}")
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,15 @@
1
+ module Nexo
2
+ class FolderDownloadJob < BaseJob
3
+ def perform(folder, type)
4
+ if type == "full_sync"
5
+ GoogleCalendarSyncService.new(folder.integration).full_sync!(folder)
6
+ elsif type == "incremental_sync"
7
+ GoogleCalendarSyncService.new(folder.integration).incremental_sync!(folder)
8
+ elsif type == "full_or_incremental_sync"
9
+ GoogleCalendarSyncService.new(folder.integration).full_or_incremental_sync!(folder)
10
+ else
11
+ raise "unknown sync type"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,6 @@
1
1
  module Nexo
2
2
  class FolderSyncJob < BaseJob
3
+ # TODO!: limit concurrency
3
4
  def perform(folder)
4
5
  protocol_service = ServiceBuilder.instance.build_protocol_service(folder)
5
6
  if folder.external_identifier.blank?
@@ -10,18 +11,23 @@ module Nexo
10
11
  end
11
12
 
12
13
  policies = PolicyService.instance.policies_for(folder)
14
+ Nexo.logger.debug { "Found #{policies.length} policies" }
13
15
  # flat_map should be equivalent to:
14
16
  # policies.map(&:synchronizable_queries).flatten(1)
15
17
  queries = policies.flat_map(&:synchronizable_queries)
18
+ Nexo.logger.debug { "Found #{queries.length} queries" }
16
19
 
17
20
  GoodJob::Bulk.enqueue do
18
21
  queries.each do |query|
19
22
  # TODO: avoid calling more than once per synchronizable
20
23
  query.find_each do |synchronizable|
24
+ Nexo.logger.debug { "Processing synchronizable: #{synchronizable}" }
25
+
21
26
  folder_service.find_element_and_sync(folder, synchronizable)
22
27
  end
23
28
  end
24
29
  end
30
+ Nexo.logger.debug { "Finished processing queries" }
25
31
  end
26
32
 
27
33
  private
@@ -1,15 +1,30 @@
1
1
  module Nexo
2
2
  class SynchronizableChangedJob < BaseJob
3
- limits_concurrency key: ->(synchronizable) { synchronizable.to_gid }
3
+ # :nocov: tricky
4
+ if defined? GoodJob
5
+ include GoodJob::ActiveJobExtensions::Concurrency
6
+
7
+ good_job_control_concurrency_with(
8
+ perform_limit: 1,
9
+ key: -> { arguments.first.to_gid.to_s }
10
+ )
11
+ end
12
+ # :nocov:
4
13
 
5
14
  # TODO: check
6
15
  # https://github.com/rails/solid_queue?tab=readme-ov-file#jobs-and-transactional-integrity
7
16
  #
8
17
  # TODO: handle exceptions
9
18
 
10
- def perform(synchronizable)
19
+ # @raise [ActiveRecord::PreparedStatementCacheExpired]
20
+ def perform(synchronizable, excluded_folders: [])
11
21
  # Maybe restrict this query to a more specific scope
12
22
  scope = Folder.kept
23
+ if excluded_folders.any?
24
+ scope = scope.where.not(id: excluded_folders)
25
+ end
26
+ Nexo.logger.debug("Processing #{scope.count} folders")
27
+
13
28
  # TODO: test
14
29
  GoodJob::Bulk.enqueue do
15
30
  scope.each do |folder|
@@ -2,34 +2,74 @@ module Nexo
2
2
  class UpdateRemoteResourceJob < BaseJob
3
3
  include ApiClients
4
4
 
5
- attr_reader :element
5
+ # https://github.com/rails/solid_queue?tab=readme-ov-file#jobs-and-transactional-integrity
6
+ self.enqueue_after_transaction_commit = true
6
7
 
7
- def perform(element)
8
- @element = element
8
+ attr_reader :element, :element_version
9
+
10
+ # @raise Google::Apis::ClientError
11
+ # @raise [ActiveRecord::RecordNotUnique] on ElementVersion update
12
+ # @raise [Errno::ENETUNREACH]
13
+ # - (Network is unreachable - Network is unreachable - connect(2) for "www.googleapis.com"
14
+ # @raise [Signet::AuthorizationError]
15
+ # - Unexpected error: #<Faraday::ConnectionFailed
16
+ # wrapped=#<Socket::ResolutionError: Failed to open TCP connection to
17
+ # oauth2.googleapis.com:443 (getaddrinfo: Name or service not known)>>"
18
+ def perform(element_version)
19
+ @element_version = element_version
20
+ @element = element_version.element
9
21
 
10
22
  validate_element_state!
11
23
 
12
24
  remote_service = ServiceBuilder.instance.build_protocol_service(element.folder)
13
25
 
14
26
  response =
15
- if element.element_versions.any?
27
+ if element.uuid.present?
16
28
  remote_service.update(element)
17
29
  else
18
- remote_service.insert(element.folder, element.synchronizable).tap do |response|
19
- element.update(uuid: response.id)
30
+ remote_service.insert(element).tap do |response|
31
+ ElementService.new(element:).update_element!(uuid: response.id)
20
32
  end
21
33
  end
22
34
 
23
- save_element_version(response)
35
+ ElementService.new(element_version:).update_element_version!(
36
+ nev_status: :synced,
37
+ etag: response.etag,
38
+ payload: response.payload
39
+ )
40
+ rescue Errors::ConflictingRemoteElementChange => e
41
+ Nexo.logger.warn <<~STR
42
+ ConflictingRemoteElementChange for #{element.to_gid}. \
43
+ Enqueuing FetchRemoteResourceJob, which should lead to conflicted element
44
+ STR
45
+ FetchRemoteResourceJob.perform_later(element)
24
46
  end
25
47
 
26
48
  private
27
49
 
28
50
  def validate_element_state!
51
+ if element.element_versions.where(nev_status: :synced)
52
+ .where("sequence > ?", element_version.sequence).any?
53
+
54
+ ElementService.new(element_version:).update_element_version!(nev_status: :superseded)
55
+
56
+ raise Errors::Error, "version superseded"
57
+ end
58
+
59
+ unless element.folder.sync_internal_changes?
60
+ raise Errors::Error, "sync direction excludes internal changes"
61
+ end
62
+
29
63
  if element.synchronizable.blank?
30
64
  raise Errors::SynchronizableNotFound
31
65
  end
32
66
 
67
+ element.synchronizable.validate_synchronizable!
68
+
69
+ if element.discarded?
70
+ raise Errors::ElementDiscarded
71
+ end
72
+
33
73
  if element.synchronizable.respond_to?(:discarded?) && element.synchronizable.discarded?
34
74
  raise Errors::SynchronizableDiscarded
35
75
  end
@@ -39,30 +79,24 @@ module Nexo
39
79
  end
40
80
 
41
81
  if element.synchronizable.conflicted?
42
- raise Errors::ElementConflicted
82
+ raise Errors::UpdateRemoteVersionFailed, "synchronizable conflicted"
43
83
  end
44
84
 
45
- if element.external_unsynced_change?
46
- raise Errors::ExternalUnsyncedChange
85
+ unless element.pending_local_sync?
86
+ raise Errors::Error, "invalid ne_status: #{element.ne_status}"
47
87
  end
48
88
 
49
- current_sequence = element.synchronizable.sequence
50
- last_synced_sequence = element.last_synced_sequence
89
+ if !element_version.internal?
90
+ raise Errors::Error, "invalid ElementVersion: must be internal"
91
+ end
51
92
 
52
- unless current_sequence > last_synced_sequence
53
- raise Errors::ElementAlreadySynced
93
+ if element_version.etag.present?
94
+ raise Errors::Error, "invalid ElementVersion: etag must be blank"
54
95
  end
55
- end
56
96
 
57
- # @todo sequence should be fetched before to avoid being outdated
58
- def save_element_version(service_response)
59
- ElementVersion.create!(
60
- element:,
61
- origin: :internal,
62
- etag: service_response.etag,
63
- payload: service_response.payload,
64
- sequence: element.synchronizable.sequence,
65
- )
97
+ unless element_version.pending_sync?
98
+ raise Errors::Error, "invalid ElementVersion: must be pending_sync"
99
+ end
66
100
  end
67
101
  end
68
102
  end
@@ -17,7 +17,7 @@ module Nexo
17
17
  def store(integration, token)
18
18
  ActiveRecord::Base.transaction do
19
19
  # Maybe these should be destroyed
20
- integration.tokens.active.update_all(tpt_status: :expired)
20
+ integration.tokens.active.update_all(nt_status: :expired)
21
21
 
22
22
  Token.create!(integration:, secret: token)
23
23
  end
@@ -28,7 +28,7 @@ module Nexo
28
28
  token = find_by_id(id)
29
29
 
30
30
  if token.present?
31
- token.update!(tpt_status: :revoked)
31
+ token.update!(nt_status: :revoked)
32
32
  else
33
33
  # TODO: pg_warn("Couldn't find token for revocation: #{id}")
34
34
  end
@@ -37,7 +37,7 @@ module Nexo
37
37
  private
38
38
 
39
39
  def find_by_id(id)
40
- Token.where(environment: Rails.env, integration: id, tpt_status: :active).last
40
+ Token.where(environment: Rails.env, integration: id, nt_status: :active).last
41
41
  end
42
42
  end
43
43
  end
@@ -53,21 +53,26 @@ module Nexo
53
53
  # Guarda el Token
54
54
  # Si el client tiene más permisos que los que el user solicitó
55
55
  def get_credentials(request = nil)
56
+ # :nocov: tricky
56
57
  if request.present? && request.session["code_verifier"].present?
57
- # :nocov: tricky
58
58
  authorizer.code_verifier = request.session["code_verifier"]
59
- # :nocov:
60
59
  end
61
- authorizer.get_credentials @integration, request
60
+ # :nocov:
61
+
62
+ authorizer.get_credentials(@integration, request).tap do |credentials|
63
+ if credentials.nil? && request.present? && !request.session["code_verifier"].present?
64
+ Nexo.logger.warn("Request has no code_verifier")
65
+ end
66
+ end
62
67
  rescue Signet::AuthorizationError
63
68
  # TODO: log
64
69
  end
65
70
 
66
- def get_authorization_url(request)
71
+ def get_authorization_url(request, login_hint: nil)
67
72
  request.session["code_verifier"] ||= Google::Auth::WebUserAuthorizer.generate_code_verifier
68
73
  authorizer.code_verifier = request.session["code_verifier"]
69
- authorizer.get_authorization_url(request:)
70
- # authorizer.get_authorization_url(request:, login_hint: "bla@gmail.com")
74
+ # authorizer.get_authorization_url(request:)
75
+ authorizer.get_authorization_url(request:, login_hint:)
71
76
  end
72
77
 
73
78
  private