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.
- checksums.yaml +4 -4
- data/README.md +11 -0
- data/app/controllers/nexo/element_versions_controller.rb +28 -0
- data/app/controllers/nexo/elements_controller.rb +64 -0
- data/app/controllers/nexo/folders_controller.rb +41 -0
- data/app/controllers/nexo/nexo_controller.rb +5 -0
- data/app/jobs/nexo/api_clients.rb +20 -18
- data/app/jobs/nexo/base_job.rb +5 -3
- data/app/jobs/nexo/delete_remote_resource_job.rb +11 -1
- data/app/jobs/nexo/fetch_remote_resource_job.rb +61 -0
- data/app/jobs/nexo/folder_check_status_job.rb +10 -0
- data/app/jobs/nexo/folder_destroy_job.rb +2 -1
- data/app/jobs/nexo/folder_download_job.rb +15 -0
- data/app/jobs/nexo/folder_sync_job.rb +6 -0
- data/app/jobs/nexo/synchronizable_changed_job.rb +17 -2
- data/app/jobs/nexo/update_remote_resource_job.rb +58 -24
- data/app/lib/nexo/active_record_google_token_store.rb +3 -3
- data/app/lib/nexo/api_client/google_auth_service.rb +11 -6
- data/app/lib/nexo/api_client/google_calendar_service.rb +167 -32
- data/app/lib/nexo/api_client/google_calendar_sync_service.rb +84 -0
- data/app/lib/nexo/element_service.rb +236 -0
- data/app/lib/nexo/errors.rb +6 -4
- data/app/lib/nexo/event_receiver.rb +4 -0
- data/app/lib/nexo/folder_service.rb +55 -28
- data/app/lib/nexo/import_remote_element_version.rb +75 -0
- data/app/lib/nexo/policy_service.rb +27 -8
- data/app/models/concerns/nexo/calendar_event.rb +33 -2
- data/app/models/concerns/nexo/synchronizable.rb +25 -9
- data/app/models/nexo/client.rb +9 -10
- data/app/models/nexo/element.rb +22 -39
- data/app/models/nexo/element_version.rb +12 -2
- data/app/models/nexo/folder.rb +29 -17
- data/app/models/nexo/integration.rb +4 -3
- data/app/models/nexo/token.rb +6 -4
- data/app/views/layouts/nexo.html.erb +42 -0
- data/app/views/nexo/element_versions/show.html.erb +16 -0
- data/app/views/nexo/elements/index.html.erb +32 -0
- data/app/views/nexo/elements/show.html.erb +56 -0
- data/app/views/nexo/folders/index.html.erb +22 -0
- data/app/views/nexo/folders/show.html.erb +22 -0
- data/config/environment.rb +1 -0
- data/config/routes.rb +25 -0
- data/config/spring.rb +1 -0
- data/db/migrate/20250505192315_create_nexo_clients.rb +1 -1
- data/db/migrate/20250506125057_create_nexo_tokens.rb +1 -1
- data/db/migrate/20250512025950_create_nexo_elements.rb +2 -2
- data/db/migrate/20250604124821_element_sync_status.rb +11 -0
- data/db/migrate/20250612002919_google_sync_tokens.rb +5 -0
- data/db/migrate/20250623132502_folder_sync_direction.rb +8 -0
- data/db/migrate/20250718012839_synchronizable_nullable.rb +6 -0
- data/db/seeds.rb +5 -4
- data/lib/nexo/engine.rb +22 -5
- data/lib/nexo/version.rb +1 -1
- metadata +38 -4
- data/app/jobs/nexo/sync_element_job.rb +0 -93
- 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
|
-
# -
|
7
|
-
# -
|
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
|
-
|
11
|
-
|
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
|
-
|
22
|
-
|
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
|
-
|
34
|
+
scope = scope.where.not(id: exclude_elements)
|
25
35
|
end
|
26
|
-
end
|
27
36
|
|
28
|
-
|
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
|
-
|
31
|
-
|
43
|
+
ElementService.new(element:).flag_for_removal!(reason)
|
44
|
+
|
45
|
+
DeleteRemoteResourceJob.perform_later(element)
|
46
|
+
end
|
32
47
|
end
|
33
48
|
|
34
|
-
|
35
|
-
must_be_included = folder.policy_match?(synchronizable)
|
49
|
+
private
|
36
50
|
|
37
|
-
|
38
|
-
|
39
|
-
synchronizable:,
|
40
|
-
folder:
|
41
|
-
)
|
51
|
+
def validate_synchronizable!(synchronizable)
|
52
|
+
synchronizable.validate_synchronizable!
|
42
53
|
|
43
|
-
|
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
|
-
|
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
|
-
|
51
|
-
raise Nexo::Errors::ElementConflicted, element
|
75
|
+
DeleteRemoteResourceJob.perform_later(element)
|
52
76
|
end
|
77
|
+
end
|
53
78
|
|
54
|
-
|
55
|
-
|
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
|
-
|
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
|
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
|
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
|
25
|
+
def applies?(folder, synchronizable)
|
22
26
|
policies = policies_for(folder)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
aplicable_policy.
|
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:
|
62
|
-
def
|
63
|
-
|
64
|
-
|
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
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
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
|
data/app/models/nexo/client.rb
CHANGED
@@ -2,14 +2,13 @@
|
|
2
2
|
#
|
3
3
|
# Table name: nexo_clients
|
4
4
|
#
|
5
|
-
# id
|
6
|
-
# service
|
7
|
-
# secret
|
8
|
-
#
|
9
|
-
# brand_name
|
10
|
-
#
|
11
|
-
#
|
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 :
|
29
|
+
enum :nc_status, authorized: 0, disabled: 1, expired: 2
|
31
30
|
|
32
|
-
validates :service, :
|
31
|
+
validates :service, :nc_status, :secret, presence: true
|
33
32
|
|
34
33
|
serialize :secret, coder: JSON
|
35
34
|
|
data/app/models/nexo/element.rb
CHANGED
@@ -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
|
8
|
-
# synchronizable_type :string
|
7
|
+
# synchronizable_id :integer
|
8
|
+
# synchronizable_type :string
|
9
9
|
# uuid :string
|
10
|
-
#
|
11
|
-
#
|
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
|
-
|
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.
|
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 :
|
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(
|
33
|
-
|
34
|
-
def policy_still_match?
|
35
|
-
folder.policy_match?(synchronizable)
|
36
|
-
end
|
37
|
+
scope :conflicted, -> { where(ne_status: :conflicted) }
|
37
38
|
|
38
|
-
def
|
39
|
-
|
39
|
+
def policy_still_applies?
|
40
|
+
# :nocov: TODO
|
41
|
+
folder.policy_applies?(synchronizable)
|
42
|
+
# :nocov:
|
40
43
|
end
|
41
44
|
|
42
|
-
def
|
43
|
-
|
45
|
+
def etag
|
46
|
+
last_remote_version&.etag
|
44
47
|
end
|
45
48
|
|
46
|
-
def
|
47
|
-
element_versions.where(
|
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
|
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 :
|
46
|
+
validates :origin, presence: true
|
37
47
|
end
|
38
48
|
end
|
data/app/models/nexo/folder.rb
CHANGED
@@ -2,37 +2,45 @@
|
|
2
2
|
#
|
3
3
|
# Table name: nexo_folders
|
4
4
|
#
|
5
|
-
# id
|
6
|
-
# integration_id
|
7
|
-
#
|
8
|
-
# external_identifier
|
9
|
-
# name
|
10
|
-
# description
|
11
|
-
# discarded_at
|
12
|
-
# created_at
|
13
|
-
# updated_at
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
31
|
-
|
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
|
data/app/models/nexo/token.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# id :bigint not null, primary key
|
6
6
|
# integration_id :bigint not null
|
7
7
|
# secret :string
|
8
|
-
#
|
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.
|
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 :
|
27
|
+
enum :nt_status, active: 0, revoked: 1, expired: 2
|
26
28
|
|
27
|
-
validates :secret, :
|
29
|
+
validates :secret, :nt_status, :environment, presence: true
|
28
30
|
end
|
29
31
|
end
|