Skip to content

Commit 0282cf0

Browse files
authored
feat(signage/playlists): add approvals (#409)
1 parent 7c4b24b commit 0282cf0

5 files changed

Lines changed: 216 additions & 9 deletions

File tree

OPENAPI_DOC.yml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15750,6 +15750,75 @@ paths:
1575015750
application/json:
1575115751
schema:
1575215752
$ref: '#/components/schemas/PlaceOS__Api__Application__ContentError'
15753+
/api/engine/v2/signage/playlists/{id}/approve:
15754+
post:
15755+
summary: approve a playlist for publication on displays
15756+
tags:
15757+
- Playlist
15758+
operationId: PlaceOS::Api::Playlist_approve_media
15759+
parameters:
15760+
- name: id
15761+
in: path
15762+
required: true
15763+
schema:
15764+
type: string
15765+
responses:
15766+
200:
15767+
description: OK
15768+
content:
15769+
application/json:
15770+
schema:
15771+
$ref: '#/components/schemas/Bool'
15772+
409:
15773+
description: Conflict
15774+
content:
15775+
application/json:
15776+
schema:
15777+
$ref: '#/components/schemas/PlaceOS__Api__Application__CommonError'
15778+
401:
15779+
description: Unauthorized
15780+
content:
15781+
application/json:
15782+
schema:
15783+
$ref: '#/components/schemas/PlaceOS__Api__Application__CommonError'
15784+
403:
15785+
description: Forbidden
15786+
404:
15787+
description: Not Found
15788+
content:
15789+
application/json:
15790+
schema:
15791+
$ref: '#/components/schemas/PlaceOS__Api__Application__CommonError'
15792+
408:
15793+
description: Request Timeout
15794+
content:
15795+
application/json:
15796+
schema:
15797+
$ref: '#/components/schemas/PlaceOS__Api__Application__CommonError'
15798+
400:
15799+
description: Bad Request
15800+
content:
15801+
application/json:
15802+
schema:
15803+
$ref: '#/components/schemas/PlaceOS__Api__Application__ParameterError'
15804+
422:
15805+
description: Unprocessable Entity
15806+
content:
15807+
application/json:
15808+
schema:
15809+
$ref: '#/components/schemas/PlaceOS__Api__Application__ParameterError'
15810+
406:
15811+
description: Not Acceptable
15812+
content:
15813+
application/json:
15814+
schema:
15815+
$ref: '#/components/schemas/PlaceOS__Api__Application__ContentError'
15816+
415:
15817+
description: Unsupported Media Type
15818+
content:
15819+
application/json:
15820+
schema:
15821+
$ref: '#/components/schemas/PlaceOS__Api__Application__ContentError'
1575315822
/api/engine/v2/signage/playlists/{id}/media:
1575415823
get:
1575515824
summary: get the current list of media for the playlist
@@ -21240,6 +21309,10 @@ components:
2124021309
type: integer
2124121310
format: Int64
2124221311
nullable: true
21312+
logged_out_at:
21313+
type: string
21314+
format: date-time
21315+
nullable: true
2124321316
work_preferences:
2124421317
type: array
2124521318
items:
@@ -22484,6 +22557,13 @@ components:
2248422557
makebreak:
2248522558
type: boolean
2248622559
nullable: true
22560+
analytics_enabled:
22561+
type: boolean
22562+
nullable: true
22563+
analytics_interval_minutes:
22564+
type: integer
22565+
format: Int32
22566+
nullable: true
2248722567
uri:
2248822568
type: string
2248922569
nullable: true
@@ -23362,6 +23442,13 @@ components:
2336223442
makebreak:
2336323443
type: boolean
2336423444
nullable: true
23445+
analytics_enabled:
23446+
type: boolean
23447+
nullable: true
23448+
analytics_interval_minutes:
23449+
type: integer
23450+
format: Int32
23451+
nullable: true
2336523452
uri:
2336623453
type: string
2336723454
nullable: true
@@ -25327,6 +25414,19 @@ components:
2532725414
user_name:
2532825415
type: string
2532925416
nullable: true
25417+
approved:
25418+
type: boolean
25419+
nullable: true
25420+
approved_by_id:
25421+
type: string
25422+
nullable: true
25423+
approved_by_name:
25424+
type: string
25425+
nullable: true
25426+
approved_by_email:
25427+
type: string
25428+
format: email
25429+
nullable: true
2533025430
items:
2533125431
type: array
2533225432
items:
@@ -25793,6 +25893,13 @@ components:
2579325893
makebreak:
2579425894
type: boolean
2579525895
nullable: true
25896+
analytics_enabled:
25897+
type: boolean
25898+
nullable: true
25899+
analytics_interval_minutes:
25900+
type: integer
25901+
format: Int32
25902+
nullable: true
2579625903
uri:
2579725904
type: string
2579825905
nullable: true
@@ -27923,6 +28030,10 @@ components:
2792328030
type: integer
2792428031
format: Int64
2792528032
nullable: true
28033+
logged_out_at:
28034+
type: string
28035+
format: date-time
28036+
nullable: true
2792628037
work_preferences:
2792728038
type: array
2792828039
items:
@@ -28506,6 +28617,13 @@ components:
2850628617
makebreak:
2850728618
type: boolean
2850828619
nullable: true
28620+
analytics_enabled:
28621+
type: boolean
28622+
nullable: true
28623+
analytics_interval_minutes:
28624+
type: integer
28625+
format: Int32
28626+
nullable: true
2850928627
uri:
2851028628
type: string
2851128629
nullable: true

shard.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,15 +207,15 @@ shards:
207207

208208
placeos-frontend-loader:
209209
git: https://github.com/placeos/frontend-loader.git
210-
version: 2.7.1+git.commit.b304dfada352b629a1bebed20583af3a00dd947e
210+
version: 2.7.1+git.commit.bb0732a60a0a098e84d4affde4867f9cbe2c470c
211211

212212
placeos-log-backend:
213213
git: https://github.com/place-labs/log-backend.git
214214
version: 0.13.0
215215

216216
placeos-models:
217217
git: https://github.com/placeos/models.git
218-
version: 9.72.0
218+
version: 9.72.2
219219

220220
placeos-resource:
221221
git: https://github.com/place-labs/resource.git
@@ -263,7 +263,7 @@ shards:
263263

264264
search-ingest:
265265
git: https://github.com/placeos/search-ingest.git
266-
version: 2.11.2+git.commit.0ce589c38e8cabcba8870a3f2592be3b87c48c41
266+
version: 2.11.2+git.commit.2d95f483042dbb34439daa42ba9f2317446d6853
267267

268268
secrets-env: # Overridden
269269
git: https://github.com/spider-gazelle/secrets-env.git

spec/controllers/signage_spec.cr

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module PlaceOS::Api
2222
item2_id = item2.id.as(String)
2323

2424
revision.items = [item1_id, item2_id]
25+
revision.approved = true
2526
revision.save!
2627
playlist = revision.playlist.as(Model::Playlist)
2728
playlist_id = playlist.id.as(String)
@@ -67,6 +68,7 @@ module PlaceOS::Api
6768
item2_id = item2.id.as(String)
6869

6970
revision.items = [item1_id, item2_id]
71+
revision.approved = true
7072
revision.save!
7173
playlist = revision.playlist.as(Model::Playlist)
7274
playlist_id = playlist.id.as(String)
@@ -106,6 +108,74 @@ module PlaceOS::Api
106108
item2.reload!
107109
item2.play_count.should eq 2
108110
end
111+
112+
it "can approve digital signage playlists" do
113+
revision = Model::Generator.revision
114+
115+
item = Model::Generator.item
116+
item.save!
117+
item1_id = item.id.as(String)
118+
item2 = Model::Generator.item
119+
item2.save!
120+
item2_id = item2.id.as(String)
121+
122+
revision.items = [item1_id, item2_id]
123+
revision.save!
124+
revision.approved.should be_false
125+
playlist = revision.playlist.as(Model::Playlist)
126+
playlist_id = playlist.id.as(String)
127+
128+
system = Model::Generator.control_system
129+
system.signage = true
130+
system.playlists = [playlist_id]
131+
system.save!
132+
system_id = system.id.as(String)
133+
134+
headers = Spec::Authentication.headers
135+
136+
result = client.get(
137+
path: "#{Signage.base_route}/#{system_id}",
138+
headers: headers,
139+
)
140+
141+
json = JSON.parse result.body
142+
json["playlist_mappings"].should eq({system_id => [playlist_id]})
143+
json["playlist_config"][playlist_id][0]["id"].should eq playlist_id
144+
json["playlist_config"][playlist_id][1].should eq [] of String
145+
146+
# skip forward a moment to avoid a 304
147+
sleep 1
148+
149+
# we should now approve the playlist
150+
approved = client.post(
151+
path: "#{Signage.base_route}/playlists/#{playlist_id}/approve",
152+
headers: Spec::Authentication.headers,
153+
)
154+
approved.status_code.should eq 200
155+
156+
# revision timestamp should have changed
157+
updated_at = revision.updated_at
158+
revision.reload!
159+
revision.approved.should be_true
160+
revision.updated_at.should_not eq updated_at
161+
162+
system.playlists_last_updated.should eq revision.updated_at
163+
164+
# the route should have modified
165+
headers["If-Modified-Since"] = result.headers["Last-Modified"]
166+
result = client.get(
167+
path: "#{Signage.base_route}/#{system_id}",
168+
headers: headers,
169+
)
170+
result.status_code.should_not eq 304
171+
172+
json = JSON.parse result.body
173+
json["playlist_mappings"].should eq({system_id => [playlist_id]})
174+
json["playlist_config"][playlist_id][0]["id"].should eq playlist_id
175+
json["playlist_config"][playlist_id][1].should eq [item1_id, item2_id]
176+
json["playlist_media"][0]["id"].should eq item1_id
177+
json["playlist_media"][1]["id"].should eq item2_id
178+
end
109179
end
110180
end
111181
end

src/placeos-rest-api/controllers/signage/playlists.cr

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,21 @@ module PlaceOS::Api
3939

4040
@[AC::Route::Filter(:before_action, except: [:index, :create])]
4141
def check_access_level
42-
return if user_support?
42+
ensure_access
43+
end
4344

44-
# find the org zone
45-
org_zone_id = authority.config["org_zone"]?.try(&.as_s?)
46-
raise Error::Forbidden.new unless org_zone_id
45+
# find the org zone
46+
getter org_zone_id : String do
47+
zone_id = authority.config["org_zone"]?.try(&.as_s?)
48+
raise Error::Forbidden.new unless zone_id
49+
zone_id
50+
end
4751

52+
def ensure_access(admin : Bool = false)
53+
return if user_support?
4854
access = check_access(current_user.groups, [org_zone_id])
49-
return if access.can_manage?
50-
55+
granted = admin ? access.admin? : access.can_manage?
56+
return if granted
5157
raise Error::Forbidden.new
5258
end
5359

@@ -99,6 +105,18 @@ module PlaceOS::Api
99105
current_playlist.destroy
100106
end
101107

108+
# approve a playlist for publication on displays
109+
@[AC::Route::POST("/:id/approve")]
110+
def approve_media : Bool
111+
ensure_access(admin: true)
112+
revision = media_revisions(1).first?
113+
raise Error::NotFound.new("no media in playlist") unless revision
114+
115+
revision.approver = current_user
116+
raise Error::ModelValidation.new(revision.errors) unless revision.save
117+
true
118+
end
119+
102120
# Playlist Revisions
103121
# ==================
104122

src/placeos-rest-api/controllers/systems.cr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ module PlaceOS::Api
404404
#
405405
protected def self.module_running_state(control_system : ::PlaceOS::Model::ControlSystem, running : Bool, single_occurrence : Bool = true)
406406
modules = single_occurrence ? control_system.modules_with_single_occurrence.map(&.id) : control_system.modules
407+
return if modules.empty?
407408
::PlaceOS::Model::Module.where(id: modules, ignore_startstop: false).update_all(running: running)
408409
end
409410

0 commit comments

Comments
 (0)