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
@@ -3,60 +3,87 @@ module Nexo
3
3
  #
4
4
  # Responsabilities:
5
5
  # - Creation of Element's
6
- # - Flagging Element's for deletion
7
- # - Triggering the SyncElementJob
6
+ # - Creation of ElementVersion on local changes
7
+ # - Flagging Element's for removal
8
+ # - Enqueues UpdateRemoteResourceJob, DeleteRemoteResourceJob
8
9
  class FolderService
10
+ # @raise [ActiveRecord::RecordNotUnique] on ElementVersion creation
9
11
  def find_element_and_sync(folder, synchronizable)
10
- # TODO: handle conflicted synchronizable
11
- element = find_element(folder, synchronizable)
12
+ validate_synchronizable!(synchronizable)
13
+
14
+ element = folder.find_element(synchronizable:)
12
15
 
13
16
  if element.present?
17
+ Nexo.logger.debug { "Element found" }
18
+ validate_element_state!(element)
14
19
  sync_element(element)
15
20
  else
21
+ Nexo.logger.debug { "Element not found" }
16
22
  create_and_sync_element(folder, synchronizable)
17
23
  end
18
24
  end
19
25
 
20
- def destroy_elements(synchronizable, reason)
21
- synchronizable.nexo_elements.each do |element|
22
- element.flag_for_deletion!(reason)
26
+ def destroy_elements(synchronizable, reason, exclude_elements: [])
27
+ Nexo.logger.debug("Destroying elements for synchronizable")
28
+
29
+ scope = synchronizable.nexo_elements
30
+
31
+ if exclude_elements.any?
32
+ Nexo.logger.debug("Excluding elements: #{exclude_elements}")
23
33
 
24
- SyncElementJob.perform_later(element)
34
+ scope = scope.where.not(id: exclude_elements)
25
35
  end
26
- end
27
36
 
28
- private
37
+ scope.each do |element|
38
+ unless element.folder.sync_internal_changes?
39
+ Nexo.logger.debug("Folder dont syncs internal changes, skipping")
40
+ next
41
+ end
29
42
 
30
- def find_element(folder, synchronizable)
31
- folder.find_element(synchronizable:)
43
+ ElementService.new(element:).flag_for_removal!(reason)
44
+
45
+ DeleteRemoteResourceJob.perform_later(element)
46
+ end
32
47
  end
33
48
 
34
- def create_and_sync_element(folder, synchronizable)
35
- must_be_included = folder.policy_match?(synchronizable)
49
+ private
36
50
 
37
- if must_be_included
38
- element = Element.create!(
39
- synchronizable:,
40
- folder:
41
- )
51
+ def validate_synchronizable!(synchronizable)
52
+ synchronizable.validate_synchronizable!
42
53
 
43
- SyncElementJob.perform_later(element)
54
+ if synchronizable.sequence.nil?
55
+ raise Errors::SynchronizableSequenceIsNull
44
56
  end
45
57
  end
46
58
 
59
+ def validate_element_state!(element)
60
+ # unless element.synced?
61
+ # raise Errors::Error, <<~STR
62
+ # element ne_status is invalid: #{element.ne_status} \
63
+ # (should be synced)
64
+ # STR
65
+ # end
66
+ end
67
+
47
68
  def sync_element(element)
48
- synchronizable = element.synchronizable
69
+ if element.policy_still_applies?
70
+ ElementService.new(element:).create_internal_version_if_none!
71
+ else
72
+ Nexo.logger.debug("Flagging for removal and enqueuing DeleteRemoteResourceJob")
73
+ ElementService.new(element:).flag_for_removal!(:no_longer_included_in_folder)
49
74
 
50
- if synchronizable.conflicted?
51
- raise Nexo::Errors::ElementConflicted, element
75
+ DeleteRemoteResourceJob.perform_later(element)
52
76
  end
77
+ end
53
78
 
54
- # Check if Synchronizable still must be included in folder
55
- if !element.policy_still_match?
56
- element.flag_for_deletion!(:no_longer_included_in_folder)
57
- end
79
+ def create_and_sync_element(folder, synchronizable)
80
+ must_be_included = folder.policy_applies?(synchronizable)
58
81
 
59
- SyncElementJob.perform_later(element)
82
+ if must_be_included
83
+ ElementService.new.create_element_for!(folder, synchronizable)
84
+ else
85
+ Nexo.logger.debug { "Policy not applies, skipping creation" }
86
+ end
60
87
  end
61
88
  end
62
89
  end
@@ -0,0 +1,75 @@
1
+ module Nexo
2
+ class ImportRemoteElementVersion
3
+ class VersionSuperseded < Errors::Error; end
4
+ class ImportRemoteVersionFailed < Errors::Error; end
5
+
6
+ def perform(element_version)
7
+ validate_element_state!(element_version)
8
+
9
+ ElementService.new(element_version:).update_synchronizable!
10
+ rescue ImportRemoteVersionFailed => e
11
+ Nexo.logger.warn(e.inspect)
12
+ rescue VersionSuperseded
13
+ Nexo.logger.info("ImportRemoteElementVersion: version superseded")
14
+ end
15
+
16
+ private
17
+
18
+ def validate_element_state!(element_version)
19
+ element = element_version.element
20
+
21
+ raise Nexo::Errors::Error, "element version must be external" if element_version.internal?
22
+ raise Nexo::Errors::Error, "etag must be present" if element_version.etag.blank?
23
+
24
+ unless element_version.nev_status == "pending_sync"
25
+ raise Nexo::Errors::Error, "nev_status invalid, should be pending_sync"
26
+ end
27
+
28
+ # NOTE: this is actually very coupled to the way Google manages etag as a
29
+ # sequential number, if a new protocol or service is added in the future
30
+ # and it doesnt manage it that way, it would have to be delegated to the
31
+ # protocol service
32
+ if element.element_versions.where(nev_status: :synced)
33
+ .where("etag > ?", element_version.etag).any?
34
+
35
+ ElementService.new(element_version:).update_element_version!(nev_status: :superseded)
36
+
37
+ raise VersionSuperseded
38
+ end
39
+
40
+ if element.conflicted?
41
+ raise ImportRemoteVersionFailed, "element conflicted"
42
+ end
43
+
44
+ # if element.synchronizable.blank?
45
+ # # TODO!: this could be that an external element was restored. i.e.:
46
+ # # google calendar event cancelled and restored.
47
+ # # this should be handled in some way, maybe configurable per folder
48
+ # # options are:
49
+ # # - ignore the element
50
+ # # - create synchronizable as if it were new
51
+ # # - discard/undiscard, for this the synchronizable should have been
52
+ # # deleted
53
+ # raise ImportRemoteVersionFailed, "synchronizable not found"
54
+ # end
55
+
56
+ # :nocov: borderline
57
+ if element.discarded?
58
+ # TODO!: this could be that an external element was restored. i.e.:
59
+ # Event excluded from folder and then restored from Google Calendar
60
+ raise ImportRemoteVersionFailed, "element discarded"
61
+ end
62
+
63
+ if element.synchronizable.present?
64
+ if element.synchronizable.conflicted?
65
+ raise ImportRemoteVersionFailed, "synchronizable conflicted"
66
+ end
67
+
68
+ if element.synchronizable.sequence.nil?
69
+ raise ImportRemoteVersionFailed, "synchronizable sequence is null"
70
+ end
71
+ end
72
+ # :nocov:
73
+ end
74
+ end
75
+ end
@@ -1,9 +1,13 @@
1
1
  module Nexo
2
2
  class PolicyService
3
- def initialize
3
+ def clear_finders!
4
4
  @finders = []
5
5
  end
6
6
 
7
+ def initialize
8
+ clear_finders!
9
+ end
10
+
7
11
  @instance = new
8
12
 
9
13
  private_class_method :new
@@ -12,29 +16,44 @@ module Nexo
12
16
  @instance
13
17
  end
14
18
 
15
- def register_folder_policy_finder(&block)
19
+ def register_folder_rule_finder(&block)
16
20
  @finders << block
17
21
  end
18
22
 
19
23
  attr_reader :finders
20
24
 
21
- def match?(folder, synchronizable)
25
+ def applies?(folder, synchronizable)
22
26
  policies = policies_for(folder)
23
- matching_policies = policies.select { |policy| policy.match?(synchronizable) }
24
- if matching_policies.any?
25
- aplicable_policy = matching_policies.sort_by { |policy| policy.priority }.last
26
- aplicable_policy.sync_policy == :include
27
+ applied_policies = policies.select { |policy| policy.applies?(synchronizable) }
28
+ # :nocov: TODO
29
+ if applied_policies.any?
30
+ aplicable_policy = applied_policies.sort_by { |policy| policy.priority }.last
31
+ logger.debug { "Aplicable policy: #{aplicable_policy.inspect}" }
32
+ logger.debug { "sync_policy: #{aplicable_policy.sync_policy}" }
33
+ aplicable_policy.sync_policy.to_s == "include"
27
34
  else
35
+ logger.debug { "No applicable policies" }
28
36
  false
29
37
  end
38
+ # :nocov:
30
39
  end
31
40
 
32
41
  def policies_for(folder)
42
+ logger.debug { "Found #{@finders.length} finders" }
43
+
33
44
  aux = @finders.map do |finder|
34
45
  finder.call(folder)
35
46
  end
36
47
 
37
- aux.flatten.compact
48
+ aux.flatten.compact.tap do |ret|
49
+ logger.debug { "Found #{ret.length} policies" }
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def logger
56
+ logger = Nexo.logger
38
57
  end
39
58
  end
40
59
  end
@@ -33,6 +33,38 @@ module Nexo
33
33
  build_date_time(date_to, time_to)
34
34
  end
35
35
 
36
+ def all_day?
37
+ time_from.blank? && time_to.blank?
38
+ end
39
+
40
+ # transparent: non blocking
41
+ # opaque: blocking
42
+ def transparency
43
+ if all_day?
44
+ "transparent"
45
+ else
46
+ "opaque"
47
+ end
48
+ end
49
+
50
+ def validate_synchronizable!
51
+ unless datetime_from.present?
52
+ raise Errors::SynchronizableInvalid, "datetime_from is nil"
53
+ end
54
+
55
+ unless datetime_to.present?
56
+ raise Errors::SynchronizableInvalid, "datetime_to is nil"
57
+ end
58
+
59
+ unless datetime_from != datetime_to
60
+ raise Errors::SynchronizableInvalid, "datetime_from and datetime_to are equal"
61
+ end
62
+
63
+ unless summary.present?
64
+ raise Errors::SynchronizableInvalid, "summary is nil"
65
+ end
66
+ end
67
+
36
68
  private
37
69
 
38
70
  # @param [Date] date
@@ -40,7 +72,7 @@ module Nexo
40
72
  #
41
73
  # @return [Date, DateTime]
42
74
  def build_date_time(date, time = nil)
43
- if time.present?
75
+ if time.present? && date.present?
44
76
  DateTime.new(
45
77
  date.year,
46
78
  date.month,
@@ -55,7 +87,6 @@ module Nexo
55
87
  end
56
88
  end
57
89
 
58
-
59
90
  # TODO: refactor https://api.rubyonrails.org/classes/ActiveSupport/Concern.html
60
91
  included do
61
92
  include Nexo::Synchronizable::Associations
@@ -35,6 +35,20 @@ module Nexo
35
35
  end
36
36
  end
37
37
  end
38
+
39
+ def create_from_payload!(folder, payload)
40
+ Nexo.logger.debug("Synchronizable#create_from_payload!")
41
+ service = Nexo::ServiceBuilder.instance.build_protocol_service(folder)
42
+ fields = service.fields_from_payload(payload)
43
+
44
+ synchronizable = new
45
+ attributes = synchronizable.translate_fields(fields)
46
+ synchronizable.assign_attributes(attributes)
47
+ synchronizable.sequence = payload["sequence"]
48
+ synchronizable.save!
49
+
50
+ synchronizable
51
+ end
38
52
  end
39
53
 
40
54
  def method_missing(method_name, *, &)
@@ -58,21 +72,23 @@ module Nexo
58
72
  nexo_elements.conflicted.any?
59
73
  end
60
74
 
61
- # :nocov: TODO, not yet implemented
62
- def update_from!(element_version)
63
- transaction do
64
- # TODO: parse the element_version.payload
65
- # and set the Synchronizable fields according to the Folder#nexo_protocol
75
+ # :nocov: borderline
76
+ def validate_synchronizable!
77
+ raise "must be implemented in subclass"
78
+ end
66
79
 
67
- new_sequence = increment_sequence!
68
- element_version.update_sequence!(new_sequence)
69
- end
80
+ def update_from_fields!(fields)
81
+ raise "must be implemented in subclass"
82
+ end
83
+
84
+ def translate_fields(fields)
85
+ raise "must be implemented in subclass"
70
86
  end
71
87
  # :nocov:
72
88
 
73
89
  def increment_sequence!
74
90
  if sequence.nil?
75
- Rails.logger.warn("Synchronizable sequence is nil on increment_sequence!: #{self.to_gid}")
91
+ Nexo.logger.warn("Synchronizable sequence is nil on increment_sequence!: #{self.to_gid}")
76
92
  end
77
93
 
78
94
  # This operation is performed directly in the database without the need
@@ -2,14 +2,13 @@
2
2
  #
3
3
  # Table name: nexo_clients
4
4
  #
5
- # id :bigint not null, primary key
6
- # service :integer
7
- # secret :string
8
- # tcp_status :integer
9
- # brand_name :integer
10
- # user_integrations_allowed :boolean
11
- # created_at :datetime not null
12
- # updated_at :datetime not null
5
+ # id :bigint not null, primary key
6
+ # service :integer
7
+ # secret :string
8
+ # nc_status :integer
9
+ # brand_name :integer
10
+ # created_at :datetime not null
11
+ # updated_at :datetime not null
13
12
  #
14
13
  require "google-apis-calendar_v3"
15
14
  require "google-apis-oauth2_v2"
@@ -27,9 +26,9 @@ module Nexo
27
26
  encrypts :secret
28
27
 
29
28
  enum :service, google: 0
30
- enum :tcp_status, authorized: 0, disabled: 1, expired: 2
29
+ enum :nc_status, authorized: 0, disabled: 1, expired: 2
31
30
 
32
- validates :service, :tcp_status, :secret, presence: true
31
+ validates :service, :nc_status, :secret, presence: true
33
32
 
34
33
  serialize :secret, coder: JSON
35
34
 
@@ -4,71 +4,54 @@
4
4
  #
5
5
  # id :bigint not null, primary key
6
6
  # folder_id :bigint not null
7
- # synchronizable_id :integer not null
8
- # synchronizable_type :string not null
7
+ # synchronizable_id :integer
8
+ # synchronizable_type :string
9
9
  # uuid :string
10
- # flag_deletion :boolean not null
11
- # deletion_reason :integer
12
- # conflicted :boolean default(FALSE), not null
10
+ # flagged_for_removal :boolean not null
11
+ # removal_reason :integer
13
12
  # discarded_at :datetime
14
13
  # created_at :datetime not null
15
14
  # updated_at :datetime not null
15
+ # ne_status :integer not null
16
16
  #
17
17
  module Nexo
18
18
  class Element < ApplicationRecord
19
19
  belongs_to :folder, class_name: "Nexo::Folder"
20
- belongs_to :synchronizable, polymorphic: true
20
+
21
+ # when blank it's a brand new remote element that needs a synchronizable to
22
+ # be created
23
+ belongs_to :synchronizable, polymorphic: true, optional: true
24
+
21
25
  has_many :element_versions, dependent: :destroy, class_name: "Nexo::ElementVersion"
22
26
 
23
27
  after_initialize do
24
28
  # TODO: https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute
25
- self.flag_deletion = false if flag_deletion.nil?
29
+ self.flagged_for_removal = false if flagged_for_removal.nil?
26
30
  end
27
31
 
28
32
  scope :kept, -> { where(discarded_at: nil) }
29
33
 
30
- enum :deletion_reason, no_longer_included_in_folder: 0, synchronizable_destroyed: 1
34
+ enum :removal_reason, no_longer_included_in_folder: 0, synchronizable_destroyed: 1
35
+ enum :ne_status, synced: 0, pending_external_sync: 1, pending_local_sync: 2, conflicted: 3
31
36
 
32
- scope :conflicted, -> { where(conflicted: true) }
33
-
34
- def policy_still_match?
35
- folder.policy_match?(synchronizable)
36
- end
37
+ scope :conflicted, -> { where(ne_status: :conflicted) }
37
38
 
38
- def last_synced_sequence
39
- element_versions.pluck(:sequence).max || -1
39
+ def policy_still_applies?
40
+ # :nocov: TODO
41
+ folder.policy_applies?(synchronizable)
42
+ # :nocov:
40
43
  end
41
44
 
42
- def external_unsynced_change?
43
- last_external_unsynced_version.present?
45
+ def etag
46
+ last_remote_version&.etag
44
47
  end
45
48
 
46
- def last_external_unsynced_version
47
- element_versions.where(sequence: nil).order(created_at: :desc).first
49
+ def last_remote_version
50
+ element_versions.where.not(etag: nil).order(:etag).last
48
51
  end
49
52
 
50
- def flag_for_deletion!(deletion_reason)
51
- update!(flag_deletion: true, deletion_reason:)
52
- end
53
-
54
- def flagged_for_deletion?
55
- flag_deletion?
56
- end
57
-
58
- # :nocov: TODO, not yet being called
59
- def flag_as_conflicted!
60
- update!(conflicted: true)
61
-
62
- # TODO: log "Conflicted Element: #{element.to_gid}"
63
- end
64
- # :nocov:
65
-
66
53
  def discarded?
67
54
  discarded_at.present?
68
55
  end
69
-
70
- def discard!
71
- update!(discarded_at: Time.current)
72
- end
73
56
  end
74
57
  end
@@ -10,12 +10,16 @@
10
10
  # origin :integer not null
11
11
  # created_at :datetime not null
12
12
  # updated_at :datetime not null
13
+ # nev_status :integer not null
13
14
  #
14
15
  module Nexo
15
16
  # sequence
16
17
  #
17
18
  # cuando es null significa que el origin es external que debe ser
18
- # sincronizado. si está presente, significa que fue sinzronizado
19
+ # sincronizado. si está presente, significa que fue sincronizado
20
+ # incremental correlativa. puede ser discontinua si se generan updates
21
+ # a una frecuencia alta
22
+ # TODO!: rename to "version"
19
23
  #
20
24
  # etag
21
25
  #
@@ -30,9 +34,15 @@ module Nexo
30
34
  belongs_to :element, class_name: "Nexo::Element"
31
35
 
32
36
  enum :origin, internal: 0, external: 1
37
+ enum :nev_status,
38
+ pending_sync: 0,
39
+ synced: 1,
40
+ ignored_in_conflict: 2,
41
+ superseded: 3,
42
+ ignored_by_sync_direction: 4
33
43
 
34
44
  serialize :payload, coder: JSON
35
45
 
36
- validates :payload, :etag, :origin, presence: true
46
+ validates :origin, presence: true
37
47
  end
38
48
  end
@@ -2,37 +2,45 @@
2
2
  #
3
3
  # Table name: nexo_folders
4
4
  #
5
- # id :bigint not null, primary key
6
- # integration_id :bigint not null
7
- # protocol :integer not null
8
- # external_identifier :string
9
- # name :string
10
- # description :string
11
- # discarded_at :datetime
12
- # created_at :datetime not null
13
- # updated_at :datetime not null
5
+ # id :bigint not null, primary key
6
+ # integration_id :bigint not null
7
+ # nexo_protocol :integer not null
8
+ # external_identifier :string
9
+ # name :string
10
+ # description :string
11
+ # discarded_at :datetime
12
+ # created_at :datetime not null
13
+ # updated_at :datetime not null
14
+ # google_next_sync_token :string
15
+ # sync_direction :integer not null
16
+ # nf_status :integer default("initial"), not null
14
17
  #
15
18
  module Nexo
16
19
  class Folder < ApplicationRecord
17
20
  belongs_to :integration, class_name: "Nexo::Integration"
18
21
  has_many :elements, class_name: "Nexo::Element"
19
22
 
20
- if respond_to?(:enumerize)
21
- enumerize :nexo_protocol, in: { calendar: 0, dummy_calendar: 1 }
22
- else
23
- enum :nexo_protocol, calendar: 0, dummy_calendar: 1
23
+ enum :nexo_protocol, calendar: 0, dummy_calendar: 1
24
+ enum :sync_direction, sync_in_out: 0, sync_out_in: 1, sync_bidirectional: 2
25
+ enum :nf_status, initial: 0, ok: 1, not_found: 2
26
+
27
+ def sync_external_changes?
28
+ sync_out_in? || sync_bidirectional?
29
+ end
30
+
31
+ def sync_internal_changes?
32
+ sync_in_out? || sync_bidirectional?
24
33
  end
25
34
 
26
35
  scope :kept, -> { where(discarded_at: nil) }
27
36
 
28
37
  validates :nexo_protocol, :name, presence: true
29
38
 
30
- # TODO!: find better name
31
- # maybe policy_applies?
32
- def policy_match?(synchronizable)
33
- PolicyService.instance.match?(self, synchronizable)
39
+ def policy_applies?(synchronizable)
40
+ PolicyService.instance.applies?(self, synchronizable)
34
41
  end
35
42
 
43
+ # TODO: use find_sole_by
36
44
  def find_element(synchronizable:)
37
45
  ary = elements.where(synchronizable:, discarded_at: nil).to_a
38
46
 
@@ -54,5 +62,9 @@ module Nexo
54
62
  def discard!
55
63
  update!(discarded_at: Time.current)
56
64
  end
65
+
66
+ def to_s
67
+ name.presence || super
68
+ end
57
69
  end
58
70
  end
@@ -14,9 +14,6 @@
14
14
  #
15
15
  module Nexo
16
16
  class Integration < ApplicationRecord
17
- include Discard::Model if defined? Discard::Model
18
- include Hashid::Rails if defined? Hashid::Rails
19
-
20
17
  serialize :scope, coder: JSON
21
18
  belongs_to :user
22
19
  belongs_to :client, class_name: "Nexo::Client"
@@ -57,6 +54,10 @@ module Nexo
57
54
  end
58
55
  end
59
56
 
57
+ def token?
58
+ token_status.in? [ :active_token, :expired_token ]
59
+ end
60
+
60
61
  def credentials
61
62
  service = ServiceBuilder.instance.build_auth_service(self)
62
63
  @credentials ||= service.get_credentials
@@ -5,7 +5,7 @@
5
5
  # id :bigint not null, primary key
6
6
  # integration_id :bigint not null
7
7
  # secret :string
8
- # tpt_status :integer not null
8
+ # nt_status :integer not null
9
9
  # environment :string not null
10
10
  # created_at :datetime not null
11
11
  # updated_at :datetime not null
@@ -14,16 +14,18 @@ module Nexo
14
14
  class Token < ApplicationRecord
15
15
  belongs_to :integration, class_name: "Nexo::Integration"
16
16
 
17
+ scope :active, -> { where(nt_status: :active) }
18
+
17
19
  after_initialize do
18
20
  # TODO: https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html#method-i-attribute
19
- self.tpt_status = :active if tpt_status.nil?
21
+ self.nt_status = :active if nt_status.nil?
20
22
  self.environment = Rails.env if environment.nil?
21
23
  end
22
24
 
23
25
  encrypts :secret
24
26
 
25
- enum :tpt_status, active: 0, revoked: 1, expired: 2
27
+ enum :nt_status, active: 0, revoked: 1, expired: 2
26
28
 
27
- validates :secret, :tpt_status, :environment, presence: true
29
+ validates :secret, :nt_status, :environment, presence: true
28
30
  end
29
31
  end