zm-ruby-client 2.2.7 → 3.0.0
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/.rubocop.yml +6 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +10 -6
- data/Gemfile +1 -6
- data/README.md +10 -15
- data/VERSION +1 -1
- data/lib/zm/client/account/account.rb +35 -22
- data/lib/zm/client/account/account_aliases_collection.rb +14 -6
- data/lib/zm/client/account/account_dls_membership_collection.rb +10 -4
- data/lib/zm/client/account/account_dls_owner_collection.rb +8 -5
- data/lib/zm/client/account/account_jsns_builder.rb +31 -19
- data/lib/zm/client/account/account_jsns_initializer.rb +4 -14
- data/lib/zm/client/account/accounts_builder.rb +1 -1
- data/lib/zm/client/account/accounts_collection.rb +15 -8
- data/lib/zm/client/ace/ace.rb +5 -5
- data/lib/zm/client/ace/ace_jsns_builder.rb +8 -8
- data/lib/zm/client/ace/ace_jsns_initializer.rb +7 -7
- data/lib/zm/client/ace/aces_builder.rb +1 -1
- data/lib/zm/client/ace/aces_collection.rb +2 -2
- data/lib/zm/client/appointment/appointment.rb +27 -24
- data/lib/zm/client/appointment/appointment_jsns_builder.rb +29 -20
- data/lib/zm/client/appointment/appointment_jsns_initializer.rb +14 -13
- data/lib/zm/client/appointment/appointments_builder.rb +1 -1
- data/lib/zm/client/appointment/appointments_collection.rb +8 -7
- data/lib/zm/client/backup/backup.rb +6 -2
- data/lib/zm/client/backup/backup_jsns_initializer.rb +14 -12
- data/lib/zm/client/backup/backups_builder.rb +1 -1
- data/lib/zm/client/backup/backups_collection.rb +2 -1
- data/lib/zm/client/base/account_search_objects_collection.rb +46 -20
- data/lib/zm/client/base/admin_objects_collection.rb +22 -27
- data/lib/zm/client/base/base_account_jsns_builder.rb +1 -4
- data/lib/zm/client/base/base_jsns_initializer.rb +8 -8
- data/lib/zm/client/base/mailbox_infos_collection.rb +23 -15
- data/lib/zm/client/base/mailbox_object.rb +174 -69
- data/lib/zm/client/base/mailbox_prefs_collection.rb +4 -8
- data/lib/zm/client/base/object.rb +2 -2
- data/lib/zm/client/base/objects_builder.rb +7 -3
- data/lib/zm/client/base/objects_collection.rb +4 -13
- data/lib/zm/client/base/zimbra_attribute.rb +4 -17
- data/lib/zm/client/base/zimbra_attributes_collection.rb +16 -9
- data/lib/zm/client/base.rb +0 -1
- data/lib/zm/client/cluster/batch_request.rb +50 -0
- data/lib/zm/client/cluster/cluster.rb +49 -27
- data/lib/zm/client/cluster/cluster_config.rb +129 -40
- data/lib/zm/client/common/recipients.rb +2 -2
- data/lib/zm/client/common/utils.rb +1 -13
- data/lib/zm/client/common.rb +0 -1
- data/lib/zm/client/connector/{rest_account.rb → rest_connector.rb} +40 -50
- data/lib/zm/client/connector/soap_account.rb +19 -15
- data/lib/zm/client/connector/soap_admin.rb +9 -9
- data/lib/zm/client/connector/soap_base.rb +24 -17
- data/lib/zm/client/connector/soap_error.rb +3 -23
- data/lib/zm/client/constant.rb +18 -16
- data/lib/zm/client/contact/contact.rb +21 -40
- data/lib/zm/client/contact/contact_jsns_builder.rb +1 -1
- data/lib/zm/client/contact/contact_jsns_initializer.rb +4 -26
- data/lib/zm/client/contact/contacts_builder.rb +1 -1
- data/lib/zm/client/contact/contacts_collection.rb +4 -24
- data/lib/zm/client/contact/group_contact_jsns_builder.rb +18 -13
- data/lib/zm/client/contact/mod_group_contact.rb +6 -2
- data/lib/zm/client/cos/cos.rb +35 -11
- data/lib/zm/client/cos/cos_domains_collection.rb +1 -1
- data/lib/zm/client/cos/cos_jsns_builder.rb +22 -9
- data/lib/zm/client/cos/cos_jsns_initializer.rb +2 -6
- data/lib/zm/client/cos/cos_servers_collection.rb +5 -6
- data/lib/zm/client/cos/coses_builder.rb +1 -1
- data/lib/zm/client/cos/coses_collection.rb +6 -4
- data/lib/zm/client/datasource/datasource.rb +27 -34
- data/lib/zm/client/datasource/datasource_jsns_builder.rb +1 -1
- data/lib/zm/client/datasource/datasource_jsns_initializer.rb +5 -6
- data/lib/zm/client/datasource/datasources_builder.rb +6 -9
- data/lib/zm/client/datasource/datasources_collection.rb +6 -5
- data/lib/zm/client/distributionlist/distributionlist.rb +22 -42
- data/lib/zm/client/distributionlist/distributionlist_aliases_collection.rb +14 -6
- data/lib/zm/client/distributionlist/distributionlist_jsns_builder.rb +20 -16
- data/lib/zm/client/distributionlist/distributionlist_jsns_initializer.rb +2 -11
- data/lib/zm/client/distributionlist/distributionlist_members_collection.rb +20 -12
- data/lib/zm/client/distributionlist/distributionlist_owners_collection.rb +16 -5
- data/lib/zm/client/distributionlist/distributionlists_builder.rb +1 -1
- data/lib/zm/client/distributionlist/distributionlists_collection.rb +12 -6
- data/lib/zm/client/distributionlist/dls_membership_collection.rb +12 -6
- data/lib/zm/client/document/document.rb +16 -8
- data/lib/zm/client/document/document_jsns_builder.rb +1 -1
- data/lib/zm/client/document/document_jsns_initializer.rb +27 -26
- data/lib/zm/client/document/documents_builder.rb +1 -1
- data/lib/zm/client/document/documents_collection.rb +1 -1
- data/lib/zm/client/domain/domain.rb +18 -37
- data/lib/zm/client/domain/domain_accounts_collection.rb +1 -1
- data/lib/zm/client/domain/domain_distributionlists_collection.rb +1 -1
- data/lib/zm/client/domain/domain_jsns_builder.rb +18 -9
- data/lib/zm/client/domain/domain_jsns_initializer.rb +2 -6
- data/lib/zm/client/domain/domain_resources_collection.rb +1 -1
- data/lib/zm/client/domain/domains_builder.rb +1 -1
- data/lib/zm/client/domain/domains_collection.rb +12 -6
- data/lib/zm/client/filter_rules/filter_rule.rb +0 -9
- data/lib/zm/client/filter_rules/filter_rule_jsns_initializer.rb +7 -6
- data/lib/zm/client/filter_rules/filter_rules_builder.rb +1 -1
- data/lib/zm/client/filter_rules/filter_rules_collection.rb +5 -15
- data/lib/zm/client/filter_rules/outgoing_filter_rules_collection.rb +5 -2
- data/lib/zm/client/folder/folder.rb +73 -56
- data/lib/zm/client/folder/folder_grant.rb +7 -5
- data/lib/zm/client/folder/folder_grant_jsns_builder.rb +6 -3
- data/lib/zm/client/folder/folder_grants_collection.rb +2 -4
- data/lib/zm/client/folder/folder_jsns_builder.rb +14 -7
- data/lib/zm/client/folder/folder_jsns_initializer.rb +58 -40
- data/lib/zm/client/folder/folder_retention_policies_collection.rb +4 -2
- data/lib/zm/client/folder/folders_builder.rb +1 -1
- data/lib/zm/client/folder/folders_collection.rb +21 -18
- data/lib/zm/client/folder/folders_jsns_builder.rb +4 -2
- data/lib/zm/client/folder/mod_document_folder.rb +5 -4
- data/lib/zm/client/identity/identities_builder.rb +1 -1
- data/lib/zm/client/identity/identities_collection.rb +6 -5
- data/lib/zm/client/identity/identity.rb +9 -29
- data/lib/zm/client/identity/identity_jsns_builder.rb +8 -4
- data/lib/zm/client/identity/identity_jsns_initializer.rb +5 -4
- data/lib/zm/client/license/license.rb +7 -1
- data/lib/zm/client/license/license_jsns_initializer.rb +8 -5
- data/lib/zm/client/license/licenses_collection.rb +1 -1
- data/lib/zm/client/mailbox/mailbox_item_concern.rb +33 -0
- data/lib/zm/client/mailbox/mailbox_item_id.rb +7 -0
- data/lib/zm/client/message/attachments_collection.rb +2 -2
- data/lib/zm/client/message/message.rb +56 -52
- data/lib/zm/client/message/message_flags.rb +21 -13
- data/lib/zm/client/message/message_jsns_builder.rb +11 -6
- data/lib/zm/client/message/message_jsns_initializer.rb +24 -23
- data/lib/zm/client/message/messages_builder.rb +1 -1
- data/lib/zm/client/message/messages_collection.rb +1 -15
- data/lib/zm/client/mountpoint/mountpoint.rb +17 -25
- data/lib/zm/client/mountpoint/mountpoint_jsns_builder.rb +6 -3
- data/lib/zm/client/mountpoint/mountpoint_jsns_initializer.rb +25 -24
- data/lib/zm/client/mountpoint/mountpoints_builder.rb +1 -1
- data/lib/zm/client/mountpoint/mountpoints_collection.rb +13 -9
- data/lib/zm/client/mta_queue/mta_queue.rb +6 -2
- data/lib/zm/client/mta_queue/mta_queue_jsns_initializer.rb +3 -7
- data/lib/zm/client/mta_queue/mta_queues_builder.rb +3 -1
- data/lib/zm/client/mta_queue_item/mta_queue_item.rb +5 -3
- data/lib/zm/client/mta_queue_item/mta_queue_item_jsns_initializer.rb +1 -5
- data/lib/zm/client/mta_queue_item/mta_queue_items_builder.rb +3 -1
- data/lib/zm/client/mta_queue_item/mta_queue_items_collection.rb +3 -1
- data/lib/zm/client/resource/resource.rb +6 -18
- data/lib/zm/client/resource/resource_jsns_builder.rb +20 -13
- data/lib/zm/client/resource/resource_jsns_initializer.rb +4 -14
- data/lib/zm/client/resource/resources_builder.rb +1 -1
- data/lib/zm/client/resource/resources_collection.rb +12 -7
- data/lib/zm/client/search_folder/search_folder.rb +13 -29
- data/lib/zm/client/search_folder/search_folder_jsns_builder.rb +6 -6
- data/lib/zm/client/search_folder/search_folder_jsns_initializer.rb +19 -18
- data/lib/zm/client/search_folder/search_folders_builder.rb +1 -1
- data/lib/zm/client/search_folder/search_folders_collection.rb +6 -5
- data/lib/zm/client/server/server.rb +9 -3
- data/lib/zm/client/server/server_accounts_collection.rb +4 -2
- data/lib/zm/client/server/server_jsns_initializer.rb +1 -5
- data/lib/zm/client/server/servers_builder.rb +1 -1
- data/lib/zm/client/server/servers_collection.rb +17 -9
- data/lib/zm/client/share/share.rb +5 -3
- data/lib/zm/client/share/share_jsns_initializer.rb +15 -14
- data/lib/zm/client/share/share_mountpoints_collection.rb +3 -6
- data/lib/zm/client/share/shares_builder.rb +1 -1
- data/lib/zm/client/share/shares_collection.rb +6 -3
- data/lib/zm/client/signature/signature.rb +6 -21
- data/lib/zm/client/signature/signature_jsns_builder.rb +12 -7
- data/lib/zm/client/signature/signature_jsns_initializer.rb +5 -4
- data/lib/zm/client/signature/signatures_builder.rb +1 -1
- data/lib/zm/client/signature/signatures_collection.rb +6 -5
- data/lib/zm/client/soap_request/request_methods_admin.rb +56 -0
- data/lib/zm/client/soap_request/request_methods_mailbox.rb +60 -0
- data/lib/zm/client/soap_request/soap_constants.rb +7 -0
- data/lib/zm/client/soap_request/soap_context.rb +11 -2
- data/lib/zm/client/soap_request.rb +3 -0
- data/lib/zm/client/tag/account_object_tags_collection.rb +11 -9
- data/lib/zm/client/tag/tag.rb +8 -44
- data/lib/zm/client/tag/tag_jsns_builder.rb +4 -2
- data/lib/zm/client/tag/tag_jsns_initializer.rb +7 -6
- data/lib/zm/client/tag/tags_builder.rb +1 -1
- data/lib/zm/client/tag/tags_collection.rb +6 -5
- data/lib/zm/client/task/task.rb +6 -5
- data/lib/zm/client/task/task_jsns_initializer.rb +28 -27
- data/lib/zm/client/task/tasks_builder.rb +1 -1
- data/lib/zm/client/task/tasks_collection.rb +2 -2
- data/lib/zm/client/token.rb +52 -0
- data/lib/zm/client/upload/upload.rb +101 -95
- data/lib/zm/client/zm_logger.rb +83 -0
- data/lib/zm/client.rb +6 -1
- data/lib/zm/modules/base.rb +0 -1
- data/lib/zm/modules/belongs_to_folder.rb +6 -7
- data/lib/zm/modules/belongs_to_tag.rb +3 -1
- data/lib/zm/modules/common/zimbra-attrs.csv +3 -4
- data/lib/zm/modules/inspector.rb +2 -2
- data/lib/zm/modules/missing_method_static_collection.rb +2 -2
- data/lib/zm/support/cache/entry.rb +63 -0
- data/lib/zm/support/cache/file_store.rb +141 -0
- data/lib/zm/support/cache/null_store.rb +43 -0
- data/lib/zm/support/cache/request_strategy.rb +10 -0
- data/lib/zm/support/cache/store.rb +197 -0
- data/lib/zm/support/cache/strategy.rb +13 -0
- data/lib/zm/support/cache.rb +26 -0
- data/lib/zm-ruby-client.rb +2 -2
- data/zm-ruby-client.gemspec +10 -15
- metadata +37 -36
- data/lib/zm/client/common/token_metadata.rb +0 -44
- data/lib/zm/modules/zm_logger.rb +0 -26
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
module Zm
|
|
6
|
+
module Client
|
|
7
|
+
class ZmLogger < ::Logger
|
|
8
|
+
def initialize(*args, **kwargs)
|
|
9
|
+
super
|
|
10
|
+
@formatter ||= ZmFormatter.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def colorize!
|
|
14
|
+
extend(ZmLoggerColorized)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class ZmFormatter < ::Logger::Formatter
|
|
18
|
+
Format = "[%s] %5s : %s\n"
|
|
19
|
+
|
|
20
|
+
def call(severity, time, _, msg)
|
|
21
|
+
format(Format, format_datetime(time), severity, msg2str(msg))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module ZmLoggerColorized
|
|
26
|
+
# ANSI sequence modes
|
|
27
|
+
MODES = {
|
|
28
|
+
clear: 0,
|
|
29
|
+
bold: 1,
|
|
30
|
+
italic: 3,
|
|
31
|
+
underline: 4
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
# ANSI sequence colors
|
|
35
|
+
BLACK = "\e[30m"
|
|
36
|
+
RED = "\e[31m"
|
|
37
|
+
GREEN = "\e[32m"
|
|
38
|
+
YELLOW = "\e[33m"
|
|
39
|
+
BLUE = "\e[34m"
|
|
40
|
+
MAGENTA = "\e[35m"
|
|
41
|
+
CYAN = "\e[36m"
|
|
42
|
+
WHITE = "\e[37m"
|
|
43
|
+
|
|
44
|
+
def severity_color(severity)
|
|
45
|
+
case severity
|
|
46
|
+
when 'DEBUG'
|
|
47
|
+
CYAN
|
|
48
|
+
when 'INFO'
|
|
49
|
+
GREEN
|
|
50
|
+
when 'WARN'
|
|
51
|
+
YELLOW
|
|
52
|
+
when 'ERROR'
|
|
53
|
+
RED
|
|
54
|
+
when 'FATAL'
|
|
55
|
+
mode_code(:bold)
|
|
56
|
+
when 'ANY'
|
|
57
|
+
mode_code(:clear)
|
|
58
|
+
else
|
|
59
|
+
mode_code(:clear)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def mode_code(mode)
|
|
64
|
+
mode = mode.to_sym if mode.is_a?(String)
|
|
65
|
+
return unless (value = MODES[mode])
|
|
66
|
+
|
|
67
|
+
"\e[#{value}m"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def colorize_message(severity, str)
|
|
71
|
+
"#{severity_color(severity)}#{str}#{mode_code(:clear)}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def format_message(severity, datetime, progname, msg)
|
|
75
|
+
colorize_message(
|
|
76
|
+
severity,
|
|
77
|
+
super
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/zm/client.rb
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'date'
|
|
4
|
+
|
|
5
|
+
require 'zm/client/soap_request'
|
|
3
6
|
require 'zm/modules/inspector'
|
|
7
|
+
require 'zm/support/cache'
|
|
8
|
+
require 'zm/client/zm_logger'
|
|
4
9
|
require 'zm/client/version'
|
|
5
10
|
require 'zm/client/constant'
|
|
11
|
+
require 'zm/client/token'
|
|
6
12
|
require 'zm/client/base'
|
|
7
13
|
require 'zm/client/cluster'
|
|
8
|
-
require 'zm/client/soap_request'
|
data/lib/zm/modules/base.rb
CHANGED
|
@@ -3,11 +3,8 @@
|
|
|
3
3
|
module Zm
|
|
4
4
|
module Client
|
|
5
5
|
module BelongsToFolder
|
|
6
|
-
def folder_id
|
|
7
|
-
@l
|
|
8
|
-
end
|
|
9
|
-
|
|
10
6
|
def folder=(folder)
|
|
7
|
+
return if folder.nil?
|
|
11
8
|
return unless @l != folder.id
|
|
12
9
|
|
|
13
10
|
@l = folder.id
|
|
@@ -24,13 +21,15 @@ module Zm
|
|
|
24
21
|
|
|
25
22
|
def move!(new_folder_id)
|
|
26
23
|
new_folder_id = new_folder_id.id if new_folder_id.is_a?(Zm::Client::Folder)
|
|
27
|
-
@
|
|
24
|
+
return if new_folder_id == @l
|
|
25
|
+
|
|
26
|
+
@parent.soap_connector.invoke(jsns_builder.to_move(new_folder_id))
|
|
27
|
+
@folder = nil
|
|
28
28
|
@l = new_folder_id
|
|
29
|
-
folder!
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def trash!
|
|
33
|
-
@parent.
|
|
32
|
+
@parent.soap_connector.invoke(jsns_builder.to_trash)
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
35
|
end
|
|
@@ -700,9 +700,9 @@ zimbraDomainAggregateQuota,,,domain,single,,,cstring,,,,
|
|
|
700
700
|
zimbraDomainAggregateQuotaPolicy,,,domain,single,,,cstring,,,,
|
|
701
701
|
zimbraDomainAggregateQuotaWarnPercent,,,domain,single,,,cstring,,,,
|
|
702
702
|
zimbraDomainMandatoryMailSignatureEnabled,,,domain,single,,,cstring,,,,
|
|
703
|
-
zimbraDomainName,,,domain,single
|
|
703
|
+
zimbraDomainName,,,domain,single,,1,cstring,,,,
|
|
704
704
|
zimbraDomainStatus,,,domain,single,,,cstring,,,,
|
|
705
|
-
zimbraDomainType,,,domain,single
|
|
705
|
+
zimbraDomainType,,,domain,single,,1,cstring,,,,
|
|
706
706
|
zimbraExternalShareInvitationUrlExpiration,,,domain,single,,,cstring,,,,
|
|
707
707
|
zimbraFreebusyExchangeServerType,,,domain,single,,,cstring,,,,
|
|
708
708
|
zimbraInternalSharingCrossDomainEnabled,,,domain,single,,,cstring,,,,
|
|
@@ -734,5 +734,4 @@ zimbraRecoveryAccountCodeValidity,"account,cos,calendarResource",,,single,,,cstr
|
|
|
734
734
|
zimbraResetPasswordRecoveryCodeExpiry,"account,cos,calendarResource",,,single,,,cstring,,,,
|
|
735
735
|
zimbraShowClientTOS,"account,domain,cos,calendarResource",,,single,,,cstring,,,,
|
|
736
736
|
zimbraDistributionListSendShareMessageToNewMembers,distributionList,,,single,,,boolean,,,,
|
|
737
|
-
zimbraServiceEnabled,,,server,multi,,,cstring,,,,
|
|
738
|
-
zimbraVirtualHostname,domain,,,single,,,cstring,,,,
|
|
737
|
+
zimbraServiceEnabled,,,server,multi,,,cstring,,,,
|
data/lib/zm/modules/inspector.rb
CHANGED
|
@@ -7,12 +7,12 @@ module Zm
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def to_h
|
|
10
|
-
|
|
10
|
+
instance_variables_map.to_h
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def inspect
|
|
14
14
|
keys_str = to_h.map { |k, v| "#{k}: #{v}" }.join(', ')
|
|
15
|
-
"#{self.class}:#{format('0x00%x',
|
|
15
|
+
"#{self.class}:#{format('0x00%x', object_id << 1)} #{keys_str}"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def instance_variables_map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'msgpack'
|
|
4
|
+
|
|
5
|
+
module Zm
|
|
6
|
+
module Support
|
|
7
|
+
module Cache
|
|
8
|
+
class Entry
|
|
9
|
+
class << self
|
|
10
|
+
def factory
|
|
11
|
+
@factory ||= MessagePack::Factory.new.tap do |msgpack_factory|
|
|
12
|
+
msgpack_factory.register_type(
|
|
13
|
+
0x01,
|
|
14
|
+
self,
|
|
15
|
+
packer: ->(entry) { entry.pack.to_msgpack },
|
|
16
|
+
unpacker: lambda { |data|
|
|
17
|
+
value, expires_at, version = MessagePack.unpack(data)
|
|
18
|
+
new(Marshal.load(value), version: version, expires_at: expires_at)
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :value, :version, :expires_at
|
|
26
|
+
|
|
27
|
+
def initialize(value, version: 1, expires_in: nil, expires_at: nil)
|
|
28
|
+
@value = value
|
|
29
|
+
@version = version.to_i
|
|
30
|
+
@expires_at = expires_at&.to_f || (expires_in && (expires_in.to_f + Time.now.to_f))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_s
|
|
34
|
+
inspect
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def inspect # :nodoc:
|
|
38
|
+
"#<#{self.class.name} value=#{@value}, version=#{@version}, expires_at=#{@expires_at}>"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def mismatched?(version)
|
|
42
|
+
version && @version != version
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def expired?
|
|
46
|
+
return false unless @expires_at
|
|
47
|
+
|
|
48
|
+
@expires_at <= Time.now.to_f
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def pack
|
|
52
|
+
members = [Marshal.dump(@value), @expires_at, @version]
|
|
53
|
+
members.pop while !members.empty? && members.last.nil?
|
|
54
|
+
members
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def dump
|
|
58
|
+
self.class.factory.dump self
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Zm
|
|
6
|
+
module Support
|
|
7
|
+
module Cache
|
|
8
|
+
class FileStore < Store
|
|
9
|
+
Cache.register_storage(:file_store, self)
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def test_required_options(options)
|
|
13
|
+
options.key?(:cache_path)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :cache_path
|
|
18
|
+
|
|
19
|
+
GITKEEP_FILES = %w[.gitkeep .keep].freeze
|
|
20
|
+
|
|
21
|
+
def initialize(**options)
|
|
22
|
+
@cache_path = options.delete(:cache_path).to_s
|
|
23
|
+
super(options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def clear(_options = nil)
|
|
27
|
+
root_dirs = (Dir.children(cache_path) - GITKEEP_FILES)
|
|
28
|
+
FileUtils.rm_r(root_dirs.collect { |f| File.join(cache_path, f) })
|
|
29
|
+
rescue Errno::ENOENT, Errno::ENOTEMPTY => e
|
|
30
|
+
@logger&.error "FileStoreError (#{e}): #{e.message}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def cleanup(options = nil)
|
|
34
|
+
options = merged_options(options)
|
|
35
|
+
search_dir(cache_path) do |fname|
|
|
36
|
+
entry = read_entry(fname, **options)
|
|
37
|
+
delete_entry(fname, **options) if entry&.expired?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def inspect # :nodoc:
|
|
42
|
+
"#<#{self.class.name} cache_path=#{@cache_path}, options=#{@options.inspect}>"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def read_entry(key, **)
|
|
48
|
+
if (payload = read_serialized_entry(key, **))
|
|
49
|
+
entry = deserialize_entry(payload)
|
|
50
|
+
entry if entry.is_a?(Cache::Entry)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def read_serialized_entry(key, **)
|
|
55
|
+
File.binread(key) if File.exist?(key)
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
warn "FileStoreError (#{e}): #{e.message}"
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def write_entry(key, entry, **)
|
|
62
|
+
write_serialized_entry(key, serialize_entry(entry), **)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def write_serialized_entry(key, payload, **)
|
|
66
|
+
ensure_cache_path(File.dirname(key))
|
|
67
|
+
File.write(key, payload)
|
|
68
|
+
true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def delete_entry(key, **_options)
|
|
72
|
+
if File.exist?(key)
|
|
73
|
+
begin
|
|
74
|
+
File.delete(key)
|
|
75
|
+
delete_empty_directories(File.dirname(key))
|
|
76
|
+
true
|
|
77
|
+
rescue StandardError
|
|
78
|
+
# Just in case the error was caused by another process deleting the file first.
|
|
79
|
+
raise if File.exist?(key)
|
|
80
|
+
|
|
81
|
+
false
|
|
82
|
+
end
|
|
83
|
+
else
|
|
84
|
+
false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Translate a key into a file path.
|
|
89
|
+
def normalize_key(key, options)
|
|
90
|
+
key = super
|
|
91
|
+
|
|
92
|
+
fpaths = []
|
|
93
|
+
|
|
94
|
+
while key.length > 8
|
|
95
|
+
chars = key.chars
|
|
96
|
+
fpaths << chars.shift(8).join
|
|
97
|
+
key = chars.join
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
File.join(cache_path, *fpaths, key)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Translate a file path into a key.
|
|
104
|
+
# def file_path_key(path)
|
|
105
|
+
# fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last.delete(File::SEPARATOR)
|
|
106
|
+
# URI.decode_www_form_component(fname, Encoding::UTF_8)
|
|
107
|
+
# end
|
|
108
|
+
|
|
109
|
+
def delete_empty_directories(dir)
|
|
110
|
+
return if File.realpath(dir) == File.realpath(cache_path)
|
|
111
|
+
|
|
112
|
+
return unless Dir.empty?(dir)
|
|
113
|
+
|
|
114
|
+
begin
|
|
115
|
+
Dir.delete(dir)
|
|
116
|
+
rescue StandardError
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
delete_empty_directories(File.dirname(dir))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def ensure_cache_path(path)
|
|
123
|
+
FileUtils.makedirs(path)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def search_dir(dir, &callback)
|
|
127
|
+
return unless File.exist?(dir)
|
|
128
|
+
|
|
129
|
+
Dir.each_child(dir) do |d|
|
|
130
|
+
name = File.join(dir, d)
|
|
131
|
+
if File.directory?(name)
|
|
132
|
+
search_dir(name, &callback)
|
|
133
|
+
else
|
|
134
|
+
callback.call name
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zm
|
|
4
|
+
module Support
|
|
5
|
+
module Cache
|
|
6
|
+
class NullStore < Store
|
|
7
|
+
Cache.register_storage(:null_store, self)
|
|
8
|
+
|
|
9
|
+
def fetch(_name, _options = nil, &block)
|
|
10
|
+
block.call
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def read(_name, _options = nil)
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def write(_name, _value, _options = nil)
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(_name, _options = nil)
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def exist?(_name, _options = nil)
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def clear(_options = nil)
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def cleanup(_options = nil)
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def inspect # :nodoc:
|
|
38
|
+
"#<#{self.class.name} options=#{@options.inspect}>"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zm
|
|
4
|
+
module Support
|
|
5
|
+
module Cache
|
|
6
|
+
class Store
|
|
7
|
+
attr_reader :options
|
|
8
|
+
attr_writer :logger
|
|
9
|
+
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
@options = options
|
|
12
|
+
@coder = Cache::Entry.factory
|
|
13
|
+
@digest = OpenSSL::Digest.new('sha256')
|
|
14
|
+
@logger = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fetch(name, options = nil, &)
|
|
18
|
+
if block_given?
|
|
19
|
+
options = merged_options(options)
|
|
20
|
+
key = normalize_key(name, options)
|
|
21
|
+
|
|
22
|
+
entry = nil
|
|
23
|
+
|
|
24
|
+
unless options[:force]
|
|
25
|
+
cached_entry = read_entry(key, **options)
|
|
26
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
|
27
|
+
|
|
28
|
+
entry = nil if entry&.mismatched?(normalize_version(options))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if entry
|
|
32
|
+
@logger&.info 'Load from cache'
|
|
33
|
+
entry.value
|
|
34
|
+
else
|
|
35
|
+
save_block_result_to_cache(key, options, &)
|
|
36
|
+
end
|
|
37
|
+
elsif options && options[:force]
|
|
38
|
+
raise ArgumentError, 'Missing block: Calling `Cache#fetch` with `force: true` requires a block.'
|
|
39
|
+
else
|
|
40
|
+
read(name, options)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def read(name, options = nil)
|
|
45
|
+
options = merged_options(options)
|
|
46
|
+
key = normalize_key(name, options)
|
|
47
|
+
version = normalize_version(options)
|
|
48
|
+
|
|
49
|
+
entry = read_entry(key, **options)
|
|
50
|
+
|
|
51
|
+
return unless entry
|
|
52
|
+
|
|
53
|
+
if entry.expired? || entry.mismatched?(version)
|
|
54
|
+
delete_entry(key, **options)
|
|
55
|
+
nil
|
|
56
|
+
else
|
|
57
|
+
@logger&.info 'Load from cache'
|
|
58
|
+
entry.value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def write(name, value, options = nil)
|
|
63
|
+
options = merged_options(options)
|
|
64
|
+
key = normalize_key(name, options)
|
|
65
|
+
version = normalize_version(options)
|
|
66
|
+
|
|
67
|
+
entry = Entry.new(value, **options, version: version)
|
|
68
|
+
write_entry(key, entry, **options)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def delete(name, options = nil)
|
|
72
|
+
options = merged_options(options)
|
|
73
|
+
key = normalize_key(name, options)
|
|
74
|
+
|
|
75
|
+
delete_entry(key, **options)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def exist?(name, options = nil)
|
|
79
|
+
options = merged_options(options)
|
|
80
|
+
key = normalize_key(name, options)
|
|
81
|
+
version = normalize_version(options)
|
|
82
|
+
|
|
83
|
+
entry = read_entry(key, **options)
|
|
84
|
+
(entry && !entry.expired? && !entry.mismatched?(version)) || false
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def clear(_options = nil)
|
|
88
|
+
raise NotImplementedError
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def cleanup(_options = nil)
|
|
92
|
+
raise NotImplementedError
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def normalize_options(call_options)
|
|
98
|
+
options.merge(call_options)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def normalize_version(call_options = nil)
|
|
102
|
+
if call_options&.key?(:version)
|
|
103
|
+
call_options[:version]
|
|
104
|
+
else
|
|
105
|
+
options[:version]
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def normalize_key(key, call_options = nil)
|
|
110
|
+
namespace = if call_options&.key?(:namespace)
|
|
111
|
+
call_options[:namespace]
|
|
112
|
+
else
|
|
113
|
+
options[:namespace]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
if namespace
|
|
117
|
+
@digest.hexdigest("#{namespace}:#{key}")
|
|
118
|
+
else
|
|
119
|
+
@digest.hexdigest(key)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def read_entry(_key, **_options)
|
|
124
|
+
raise NotImplementedError
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def write_entry(_key, _entry, **_options)
|
|
128
|
+
raise NotImplementedError
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def delete_entry(_key, **_options)
|
|
132
|
+
raise NotImplementedError
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def merged_options(call_options)
|
|
136
|
+
if call_options
|
|
137
|
+
call_options = normalize_options(call_options)
|
|
138
|
+
if call_options.key?(:expires_in) && call_options.key?(:expires_at)
|
|
139
|
+
raise ArgumentError, 'Either :expires_in or :expires_at can be supplied, but not both'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
expires_at = call_options.delete(:expires_at)
|
|
143
|
+
call_options[:expires_in] = (expires_at - Time.now) if expires_at
|
|
144
|
+
|
|
145
|
+
if call_options[:expires_in].is_a?(Time)
|
|
146
|
+
expires_in = call_options[:expires_in]
|
|
147
|
+
raise ArgumentError,
|
|
148
|
+
"expires_in parameter should not be a Time. Did you mean to use expires_at? Got: #{expires_in}"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
if call_options[:expires_in]&.negative?
|
|
152
|
+
expires_in = call_options.delete(:expires_in)
|
|
153
|
+
raise ArgumentError, "Cache expiration time is invalid, cannot be negative: #{expires_in}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if options.empty?
|
|
157
|
+
call_options
|
|
158
|
+
else
|
|
159
|
+
options.merge(call_options)
|
|
160
|
+
end
|
|
161
|
+
else
|
|
162
|
+
options
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def handle_expired_entry(entry, key, options)
|
|
167
|
+
if entry&.expired?
|
|
168
|
+
delete_entry(key, **options)
|
|
169
|
+
entry = nil
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
entry
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def save_block_result_to_cache(key, options)
|
|
176
|
+
version = normalize_version(options)
|
|
177
|
+
|
|
178
|
+
value = yield
|
|
179
|
+
|
|
180
|
+
entry = Entry.new(value, **options, version: version)
|
|
181
|
+
write_entry(key, entry, **options)
|
|
182
|
+
value
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def serialize_entry(entry, **)
|
|
186
|
+
@coder.dump(entry)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def deserialize_entry(payload, **)
|
|
190
|
+
@coder.load(payload)
|
|
191
|
+
rescue DeserializationError
|
|
192
|
+
nil
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zm
|
|
4
|
+
module Support
|
|
5
|
+
module Cache
|
|
6
|
+
DeserializationError = Class.new(StandardError)
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def registered_storage
|
|
10
|
+
@registered_storage ||= {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def register_storage(key, klass)
|
|
14
|
+
registered_storage[key] = klass
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'zm/support/cache/entry'
|
|
22
|
+
require 'zm/support/cache/strategy'
|
|
23
|
+
require 'zm/support/cache/request_strategy'
|
|
24
|
+
require 'zm/support/cache/store'
|
|
25
|
+
require 'zm/support/cache/null_store'
|
|
26
|
+
require 'zm/support/cache/file_store'
|