lita-locker 0.5.3 → 0.7.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.
@@ -0,0 +1,41 @@
1
+ module Lita
2
+ module Handlers
3
+ # Event-related handlers
4
+ class LockerEvents < Handler
5
+ namespace 'Locker'
6
+
7
+ include ::Locker::Label
8
+ include ::Locker::Misc
9
+ include ::Locker::Regex
10
+ include ::Locker::Resource
11
+
12
+ on :lock_attempt, :lock_attempt
13
+ on :unlock_attempt, :unlock_attempt
14
+
15
+ def lock_attempt(payload)
16
+ label = payload[:label]
17
+ user = Lita::User.find_by_id(payload[:user_id])
18
+ request_id = payload[:request_id]
19
+
20
+ if label_exists?(label) && lock_label!(label, user, nil)
21
+ robot.trigger(:lock_success, request_id: request_id)
22
+ else
23
+ robot.trigger(:lock_failure, request_id: request_id)
24
+ end
25
+ end
26
+
27
+ def unlock_attempt(payload)
28
+ label = payload[:label]
29
+ request_id = payload[:request_id]
30
+
31
+ if label_exists?(label) && unlock_label!(label)
32
+ robot.trigger(:unlock_success, request_id: request_id)
33
+ else
34
+ robot.trigger(:unlock_failure, request_id: request_id)
35
+ end
36
+ end
37
+
38
+ Lita.register_handler(LockerEvents)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ module Lita
2
+ module Handlers
3
+ # HTTP-related handlers
4
+ class LockerHttp < Handler
5
+ namespace 'Locker'
6
+
7
+ include ::Locker::Label
8
+ include ::Locker::Misc
9
+ include ::Locker::Regex
10
+ include ::Locker::Resource
11
+
12
+ http.get '/locker/label/:name', :http_label_show
13
+ http.get '/locker/resource/:name', :http_resource_show
14
+
15
+ def http_label_show(request, response)
16
+ name = request.env['router.params'][:name]
17
+ response.headers['Content-Type'] = 'application/json'
18
+ response.write(label(name).to_json)
19
+ end
20
+
21
+ def http_resource_show(request, response)
22
+ name = request.env['router.params'][:name]
23
+ response.headers['Content-Type'] = 'application/json'
24
+ response.write(resource(name).to_json)
25
+ end
26
+
27
+ Lita.register_handler(LockerHttp)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,114 @@
1
+ module Lita
2
+ module Handlers
3
+ # Label-related handlers
4
+ class LockerLabels < Handler
5
+ namespace 'Locker'
6
+
7
+ include ::Locker::Label
8
+ include ::Locker::Misc
9
+ include ::Locker::Regex
10
+ include ::Locker::Resource
11
+
12
+ route(
13
+ /^locker\slabel\slist$/,
14
+ :list,
15
+ command: true,
16
+ help: { t('help.label.list.syntax') => t('help.label.list.desc') }
17
+ )
18
+
19
+ route(
20
+ /^locker\slabel\screate\s#{LABEL_REGEX}$/,
21
+ :create,
22
+ command: true,
23
+ help: { t('help.label.create.syntax') => t('help.label.create.desc') }
24
+ )
25
+
26
+ route(
27
+ /^locker\slabel\sdelete\s#{LABEL_REGEX}$/,
28
+ :delete,
29
+ command: true,
30
+ help: { t('help.label.delete.syntax') => t('help.label.delete.desc') }
31
+ )
32
+
33
+ route(
34
+ /^locker\slabel\sshow\s#{LABEL_REGEX}$/,
35
+ :show,
36
+ command: true,
37
+ help: { t('help.label.show.syntax') => t('help.label.show.desc') }
38
+ )
39
+
40
+ route(
41
+ /^locker\slabel\sadd\s#{RESOURCE_REGEX}\sto\s#{LABEL_REGEX}$/,
42
+ :add,
43
+ command: true,
44
+ help: { t('help.label.add.syntax') => t('help.label.add.desc') }
45
+ )
46
+
47
+ route(
48
+ /^locker\slabel\sremove\s#{RESOURCE_REGEX}\sfrom\s#{LABEL_REGEX}$/,
49
+ :remove,
50
+ command: true,
51
+ help: { t('help.label.remove.syntax') => t('help.label.remove.desc') }
52
+ )
53
+
54
+ def list(response)
55
+ labels.sort.each do |n|
56
+ name = n.sub('label_', '')
57
+ l = label(name)
58
+ response.reply(t('label.desc', name: name, state: l['state']))
59
+ end
60
+ end
61
+
62
+ def create(response)
63
+ name = response.matches[0][0]
64
+ if create_label(name)
65
+ response.reply(t('label.created', name: name))
66
+ else
67
+ response.reply(t('label.exists', name: name))
68
+ end
69
+ end
70
+
71
+ def delete(response)
72
+ name = response.matches[0][0]
73
+ if delete_label(name)
74
+ response.reply(t('label.deleted', name: name))
75
+ else
76
+ response.reply(t('label.does_not_exist', name: name))
77
+ end
78
+ end
79
+
80
+ def show(response)
81
+ name = response.matches[0][0]
82
+ return response.reply(t('label.does_not_exist', name: name)) unless label_exists?(name)
83
+ members = label_membership(name)
84
+ return response.reply(t('label.has_no_resources', name: name)) unless members.count > 0
85
+ response.reply(t('label.resources', name: name, resources: members.join(', ')))
86
+ end
87
+
88
+ def add(response)
89
+ resource_name = response.matches[0][0]
90
+ label_name = response.matches[0][1]
91
+ return response.reply(t('label.does_not_exist', name: label_name)) unless label_exists?(label_name)
92
+ return response.reply(t('resource.does_not_exist', name: resource_name)) unless resource_exists?(resource_name)
93
+ add_resource_to_label(label_name, resource_name)
94
+ response.reply(t('label.resource_added', label: label_name, resource: resource_name))
95
+ end
96
+
97
+ def remove(response)
98
+ resource_name = response.matches[0][0]
99
+ label_name = response.matches[0][1]
100
+ return response.reply(t('label.does_not_exist', name: label_name)) unless label_exists?(label_name)
101
+ return response.reply(t('resource.does_not_exist', name: resource_name)) unless resource_exists?(resource_name)
102
+ members = label_membership(label_name)
103
+ if members.include?(resource_name)
104
+ remove_resource_from_label(label_name, resource_name)
105
+ response.reply(t('label.resource_removed', label: label_name, resource: resource_name))
106
+ else
107
+ response.reply(t('label.does_not_have_resource', label: label_name, resource: resource_name))
108
+ end
109
+ end
110
+
111
+ Lita.register_handler(LockerLabels)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,62 @@
1
+ module Lita
2
+ module Handlers
3
+ # Misc Locker handlers
4
+ class LockerMisc < Handler
5
+ namespace 'Locker'
6
+
7
+ include ::Locker::Label
8
+ include ::Locker::Misc
9
+ include ::Locker::Regex
10
+ include ::Locker::Resource
11
+
12
+ route(
13
+ /^locker\sstatus\s#{LABEL_REGEX}$/,
14
+ :status,
15
+ command: true,
16
+ help: { t('help.status.syntax') => t('help.status.desc') }
17
+ )
18
+
19
+ route(
20
+ /^locker\slist\s#{USER_REGEX}$/,
21
+ :list,
22
+ command: true,
23
+ help: { t('help.list.syntax') => t('help.list.desc') }
24
+ )
25
+
26
+ def status(response)
27
+ name = response.matches[0][0]
28
+ if label_exists?(name)
29
+ l = label(name)
30
+ if l['owner_id'] && l['owner_id'] != ''
31
+ o = Lita::User.find_by_id(l['owner_id'])
32
+ response.reply(t('label.desc_owner', name: name,
33
+ state: l['state'],
34
+ owner_name: o.name))
35
+ else
36
+ response.reply(t('label.desc', name: name, state: l['state']))
37
+ end
38
+ elsif resource_exists?(name)
39
+ r = resource(name)
40
+ response.reply(t('resource.desc', name: name, state: r['state']))
41
+ else
42
+ response.reply(t('subject.does_not_exist', name: name))
43
+ end
44
+ end
45
+
46
+ def list(response)
47
+ username = response.match_data['username']
48
+ user = Lita::User.fuzzy_find(username)
49
+ return response.reply('Unknown user') unless user
50
+ l = user_locks(user)
51
+ return response.reply('That user has no active locks') unless l.size > 0
52
+ composed = ''
53
+ l.each do |label_name|
54
+ composed += "Label: #{label_name}\n"
55
+ end
56
+ response.reply(composed)
57
+ end
58
+
59
+ Lita.register_handler(LockerMisc)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,82 @@
1
+ module Lita
2
+ module Handlers
3
+ # Resource-related handlers
4
+ class LockerResources < Handler
5
+ namespace 'Locker'
6
+
7
+ include ::Locker::Label
8
+ include ::Locker::Misc
9
+ include ::Locker::Regex
10
+ include ::Locker::Resource
11
+
12
+ route(
13
+ /^locker\sresource\slist$/,
14
+ :list,
15
+ command: true,
16
+ help: { t('help.resource.list.syntax') => t('help.resource.list.desc') }
17
+ )
18
+
19
+ route(
20
+ /^locker\sresource\screate\s#{RESOURCE_REGEX}$/,
21
+ :create,
22
+ command: true,
23
+ restrict_to: [:locker_admins],
24
+ help: {
25
+ t('help.resource.create.syntax') => t('help.resource.create.desc')
26
+ }
27
+ )
28
+
29
+ route(
30
+ /^locker\sresource\sdelete\s#{RESOURCE_REGEX}$/,
31
+ :delete,
32
+ command: true,
33
+ restrict_to: [:locker_admins],
34
+ help: {
35
+ t('help.resource.delete.syntax') => t('help.resource.delete.desc')
36
+ }
37
+ )
38
+
39
+ route(
40
+ /^locker\sresource\sshow\s#{RESOURCE_REGEX}$/,
41
+ :show,
42
+ command: true,
43
+ help: { t('help.resource.show.syntax') => t('help.resource.show.desc') }
44
+ )
45
+
46
+ def list(response)
47
+ output = ''
48
+ resources.each do |r|
49
+ r_name = r.sub('resource_', '')
50
+ res = resource(r_name)
51
+ output += t('resource.desc', name: r_name, state: res['state'])
52
+ end
53
+ response.reply(output)
54
+ end
55
+
56
+ def create(response)
57
+ name = response.matches[0][0]
58
+ if create_resource(name)
59
+ response.reply(t('resource.created', name: name))
60
+ else
61
+ response.reply(t('resource.exists', name: name))
62
+ end
63
+ end
64
+
65
+ def delete(response)
66
+ name = response.matches[0][0]
67
+ return response.reply(t('resource.does_not_exist', name: name)) unless resource_exists?(name)
68
+ delete_resource(name)
69
+ response.reply(t('resource.deleted', name: name))
70
+ end
71
+
72
+ def show(response)
73
+ name = response.matches[0][0]
74
+ return response.reply(t('resource.does_not_exist', name: name)) unless resource_exists?(name)
75
+ r = resource(name)
76
+ response.reply(t('resource.desc', name: name, state: r['state']))
77
+ end
78
+
79
+ Lita.register_handler(LockerResources)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,67 @@
1
+ # Locker subsystem
2
+ module Locker
3
+ # Label helpers
4
+ module Label
5
+ def label(name)
6
+ redis.hgetall("label_#{name}")
7
+ end
8
+
9
+ def labels
10
+ redis.keys('label_*')
11
+ end
12
+
13
+ def label_exists?(name)
14
+ redis.exists("label_#{name}")
15
+ end
16
+
17
+ def lock_label!(name, owner, time_until)
18
+ return false unless label_exists?(name)
19
+ key = "label_#{name}"
20
+ members = label_membership(name)
21
+ members.each do |m|
22
+ return false unless lock_resource!(m, owner, time_until)
23
+ end
24
+ redis.hset(key, 'state', 'locked')
25
+ redis.hset(key, 'owner_id', owner.id)
26
+ redis.hset(key, 'until', time_until)
27
+ true
28
+ end
29
+
30
+ def unlock_label!(name)
31
+ return false unless label_exists?(name)
32
+ key = "label_#{name}"
33
+ members = label_membership(name)
34
+ members.each do |m|
35
+ unlock_resource!(m)
36
+ end
37
+ redis.hset(key, 'state', 'unlocked')
38
+ redis.hset(key, 'owner_id', '')
39
+ true
40
+ end
41
+
42
+ def create_label(name)
43
+ label_key = "label_#{name}"
44
+ redis.hset(label_key, 'state', 'unlocked') unless
45
+ resource_exists?(name) || label_exists?(name)
46
+ end
47
+
48
+ def delete_label(name)
49
+ label_key = "label_#{name}"
50
+ redis.del(label_key) if label_exists?(name)
51
+ end
52
+
53
+ def label_membership(name)
54
+ redis.smembers("membership_#{name}")
55
+ end
56
+
57
+ def add_resource_to_label(label, resource)
58
+ return unless label_exists?(label) && resource_exists?(resource)
59
+ redis.sadd("membership_#{label}", resource)
60
+ end
61
+
62
+ def remove_resource_from_label(label, resource)
63
+ return unless label_exists?(label) && resource_exists?(resource)
64
+ redis.srem("membership_#{label}", resource)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ # Locker subsystem
2
+ module Locker
3
+ # Misc helpers
4
+ module Misc
5
+ def user_locks(user)
6
+ owned = []
7
+ labels.each do |name|
8
+ name.slice! 'label_'
9
+ label = label(name)
10
+ owned.push(name) if label['owner_id'] == user.id
11
+ end
12
+ owned
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # Locker subsystem
2
+ module Locker
3
+ # Regex definitions
4
+ module Regex
5
+ LABEL_REGEX = /([\.\w\s-]+)/
6
+ RESOURCE_REGEX = /([\.\w-]+)/
7
+ COMMENT_REGEX = /(\s\#.+)?/
8
+ LOCK_REGEX = /\(lock\)\s/i
9
+ USER_REGEX = /(?:@)?(?<username>[\w\s]+)/
10
+ UNLOCK_REGEX = /(?:\(unlock\)|\(release\))\s/i
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ # Locker subsystem
2
+ module Locker
3
+ # Resource helpers
4
+ module Resource
5
+ def resource(name)
6
+ redis.hgetall("resource_#{name}")
7
+ end
8
+
9
+ def resources
10
+ redis.keys('resource_*')
11
+ end
12
+
13
+ def resource_exists?(name)
14
+ redis.exists("resource_#{name}")
15
+ end
16
+
17
+ def lock_resource!(name, owner, time_until)
18
+ return false unless resource_exists?(name)
19
+ resource_key = "resource_#{name}"
20
+ value = redis.hget(resource_key, 'state')
21
+ return false unless value == 'unlocked'
22
+ # FIXME: Race condition!
23
+ redis.hset(resource_key, 'state', 'locked')
24
+ redis.hset(resource_key, 'owner_id', owner.id)
25
+ redis.hset(resource_key, 'until', time_until)
26
+ true
27
+ end
28
+
29
+ def unlock_resource!(name)
30
+ return false unless resource_exists?(name)
31
+ key = "resource_#{name}"
32
+ redis.hset(key, 'state', 'unlocked')
33
+ redis.hset(key, 'owner_id', '')
34
+ true
35
+ end
36
+
37
+ def create_resource(name)
38
+ resource_key = "resource_#{name}"
39
+ redis.hset(resource_key, 'state', 'unlocked') unless
40
+ resource_exists?(name) || label_exists?(name)
41
+ end
42
+
43
+ def delete_resource(name)
44
+ resource_key = "resource_#{name}"
45
+ redis.del(resource_key) if resource_exists?(name)
46
+ end
47
+ end
48
+ end