@xiboplayer/utils 0.3.7 → 0.4.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.
- package/package.json +2 -2
- package/src/cms-api.js +801 -1
- package/src/cms-api.test.js +912 -0
- package/src/config.js +4 -0
- package/src/fetch-retry.js +46 -1
- package/src/fetch-retry.test.js +106 -0
package/src/cms-api.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* CMS API Client — OAuth2-authenticated REST client for Xibo CMS
|
|
3
3
|
*
|
|
4
4
|
* Full CRUD client for all Xibo CMS REST API entities: displays, layouts,
|
|
5
|
-
* regions, widgets, media, campaigns, schedules, display groups, resolutions
|
|
5
|
+
* regions, widgets, media, campaigns, schedules, display groups, resolutions,
|
|
6
|
+
* commands, dayparts, playlists, datasets, notifications, folders, and tags.
|
|
6
7
|
* Implements OAuth2 client_credentials flow (machine-to-machine).
|
|
7
8
|
*
|
|
8
9
|
* Usage:
|
|
@@ -747,6 +748,805 @@ export class CmsApiClient {
|
|
|
747
748
|
async editLayout(layoutId, params) {
|
|
748
749
|
return this.request('PUT', `/layout/${layoutId}`, params);
|
|
749
750
|
}
|
|
751
|
+
|
|
752
|
+
// ── Layout Copy / Discard (#25) ────────────────────────────────────
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Copy a layout
|
|
756
|
+
* @param {number} layoutId
|
|
757
|
+
* @param {Object} [opts] - Options (name, description, copyMediaFiles)
|
|
758
|
+
* @returns {Promise<Object>} Copied layout
|
|
759
|
+
*/
|
|
760
|
+
async copyLayout(layoutId, opts = {}) {
|
|
761
|
+
return this.post(`/layout/copy/${layoutId}`, opts);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Discard a draft layout (revert to last published version)
|
|
766
|
+
* @param {number} layoutId
|
|
767
|
+
* @returns {Promise<void>}
|
|
768
|
+
*/
|
|
769
|
+
async discardLayout(layoutId) {
|
|
770
|
+
await this.put(`/layout/discard/${layoutId}`);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// ── Campaign Edit / Unassign (#26) ─────────────────────────────────
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Edit campaign properties
|
|
777
|
+
* @param {number} campaignId
|
|
778
|
+
* @param {Object} params - Properties to update (name, etc.)
|
|
779
|
+
* @returns {Promise<Object>} Updated campaign
|
|
780
|
+
*/
|
|
781
|
+
async editCampaign(campaignId, params) {
|
|
782
|
+
return this.put(`/campaign/${campaignId}`, params);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Get a single campaign by ID
|
|
787
|
+
* @param {number} campaignId
|
|
788
|
+
* @returns {Promise<Object>}
|
|
789
|
+
*/
|
|
790
|
+
async getCampaign(campaignId) {
|
|
791
|
+
return this.get(`/campaign/${campaignId}`);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Unassign a layout from a campaign
|
|
796
|
+
* @param {number} campaignId
|
|
797
|
+
* @param {number} layoutId
|
|
798
|
+
* @returns {Promise<void>}
|
|
799
|
+
*/
|
|
800
|
+
async unassignLayoutFromCampaign(campaignId, layoutId) {
|
|
801
|
+
await this.post(`/campaign/layout/unassign/${campaignId}`, { layoutId });
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// ── Schedule Edit (#27) ────────────────────────────────────────────
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Edit a schedule event
|
|
808
|
+
* @param {number} eventId
|
|
809
|
+
* @param {Object} params - Properties to update (fromDt, toDt, isPriority, etc.)
|
|
810
|
+
* @returns {Promise<Object>} Updated schedule event
|
|
811
|
+
*/
|
|
812
|
+
async editSchedule(eventId, params) {
|
|
813
|
+
return this.put(`/schedule/${eventId}`, params);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// ── Layout Retire / Status / Tag (#34) ─────────────────────────────
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Retire a layout (hide from new scheduling but keep existing schedules)
|
|
820
|
+
* @param {number} layoutId
|
|
821
|
+
* @returns {Promise<void>}
|
|
822
|
+
*/
|
|
823
|
+
async retireLayout(layoutId) {
|
|
824
|
+
await this.put(`/layout/retire/${layoutId}`);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Unretire a previously retired layout
|
|
829
|
+
* @param {number} layoutId
|
|
830
|
+
* @returns {Promise<void>}
|
|
831
|
+
*/
|
|
832
|
+
async unretireLayout(layoutId) {
|
|
833
|
+
await this.put(`/layout/unretire/${layoutId}`);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Get layout status (build status, validity)
|
|
838
|
+
* @param {number} layoutId
|
|
839
|
+
* @returns {Promise<Object>}
|
|
840
|
+
*/
|
|
841
|
+
async getLayoutStatus(layoutId) {
|
|
842
|
+
return this.get(`/layout/status/${layoutId}`);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Tag a layout
|
|
847
|
+
* @param {number} layoutId
|
|
848
|
+
* @param {string[]} tags - Tag names to add
|
|
849
|
+
* @returns {Promise<void>}
|
|
850
|
+
*/
|
|
851
|
+
async tagLayout(layoutId, tags) {
|
|
852
|
+
await this.post(`/layout/${layoutId}/tag`, { tag: tags.join(',') });
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/**
|
|
856
|
+
* Remove tags from a layout
|
|
857
|
+
* @param {number} layoutId
|
|
858
|
+
* @param {string[]} tags - Tag names to remove
|
|
859
|
+
* @returns {Promise<void>}
|
|
860
|
+
*/
|
|
861
|
+
async untagLayout(layoutId, tags) {
|
|
862
|
+
await this.post(`/layout/${layoutId}/untag`, { tag: tags.join(',') });
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// ── Command CRUD (#36) ─────────────────────────────────────────────
|
|
866
|
+
|
|
867
|
+
/**
|
|
868
|
+
* List commands
|
|
869
|
+
* @param {Object} [filters] - Filters (commandId, command)
|
|
870
|
+
* @returns {Promise<Array>}
|
|
871
|
+
*/
|
|
872
|
+
async listCommands(filters = {}) {
|
|
873
|
+
const data = await this.get('/command', filters);
|
|
874
|
+
return Array.isArray(data) ? data : [];
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Create a command
|
|
879
|
+
* @param {Object} params - { command, description, code }
|
|
880
|
+
* @returns {Promise<Object>}
|
|
881
|
+
*/
|
|
882
|
+
async createCommand(params) {
|
|
883
|
+
return this.post('/command', params);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Edit a command
|
|
888
|
+
* @param {number} commandId
|
|
889
|
+
* @param {Object} params - Properties to update
|
|
890
|
+
* @returns {Promise<Object>}
|
|
891
|
+
*/
|
|
892
|
+
async editCommand(commandId, params) {
|
|
893
|
+
return this.put(`/command/${commandId}`, params);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Delete a command
|
|
898
|
+
* @param {number} commandId
|
|
899
|
+
* @returns {Promise<void>}
|
|
900
|
+
*/
|
|
901
|
+
async deleteCommand(commandId) {
|
|
902
|
+
await this.del(`/command/${commandId}`);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// ── Display Extras (#41) ───────────────────────────────────────────
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Delete a display
|
|
909
|
+
* @param {number} displayId
|
|
910
|
+
* @returns {Promise<void>}
|
|
911
|
+
*/
|
|
912
|
+
async deleteDisplay(displayId) {
|
|
913
|
+
await this.del(`/display/${displayId}`);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Send Wake-on-LAN to a display
|
|
918
|
+
* @param {number} displayId
|
|
919
|
+
* @returns {Promise<void>}
|
|
920
|
+
*/
|
|
921
|
+
async wolDisplay(displayId) {
|
|
922
|
+
await this.post(`/display/wol/${displayId}`);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* Set the default layout for a display
|
|
927
|
+
* @param {number} displayId
|
|
928
|
+
* @param {number} layoutId - Layout to set as default
|
|
929
|
+
* @returns {Promise<Object>}
|
|
930
|
+
*/
|
|
931
|
+
async setDefaultLayout(displayId, layoutId) {
|
|
932
|
+
return this.put(`/display/${displayId}`, { defaultLayoutId: layoutId });
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Purge all content from a display (force re-download)
|
|
937
|
+
* @param {number} displayId
|
|
938
|
+
* @returns {Promise<void>}
|
|
939
|
+
*/
|
|
940
|
+
async purgeDisplay(displayId) {
|
|
941
|
+
await this.post(`/display/purge/${displayId}`);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// ── DayPart CRUD (#24) ─────────────────────────────────────────────
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* List day parts
|
|
948
|
+
* @param {Object} [filters] - Filters (dayPartId, name)
|
|
949
|
+
* @returns {Promise<Array>}
|
|
950
|
+
*/
|
|
951
|
+
async listDayParts(filters = {}) {
|
|
952
|
+
const data = await this.get('/daypart', filters);
|
|
953
|
+
return Array.isArray(data) ? data : [];
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Create a day part
|
|
958
|
+
* @param {Object} params - { name, description, startTime, endTime, exceptionDays, ... }
|
|
959
|
+
* @returns {Promise<Object>}
|
|
960
|
+
*/
|
|
961
|
+
async createDayPart(params) {
|
|
962
|
+
return this.post('/daypart', params);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Edit a day part
|
|
967
|
+
* @param {number} dayPartId
|
|
968
|
+
* @param {Object} params - Properties to update
|
|
969
|
+
* @returns {Promise<Object>}
|
|
970
|
+
*/
|
|
971
|
+
async editDayPart(dayPartId, params) {
|
|
972
|
+
return this.put(`/daypart/${dayPartId}`, params);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Delete a day part
|
|
977
|
+
* @param {number} dayPartId
|
|
978
|
+
* @returns {Promise<void>}
|
|
979
|
+
*/
|
|
980
|
+
async deleteDayPart(dayPartId) {
|
|
981
|
+
await this.del(`/daypart/${dayPartId}`);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// ── Library Extensions (#33) ───────────────────────────────────────
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Upload media from a URL
|
|
988
|
+
* @param {string} url - Remote URL to download
|
|
989
|
+
* @param {string} name - Name for the media item
|
|
990
|
+
* @returns {Promise<Object>}
|
|
991
|
+
*/
|
|
992
|
+
async uploadMediaUrl(url, name) {
|
|
993
|
+
return this.post('/library', { url, name, type: 'uri' });
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Copy a media item
|
|
998
|
+
* @param {number} mediaId
|
|
999
|
+
* @returns {Promise<Object>} Copied media
|
|
1000
|
+
*/
|
|
1001
|
+
async copyMedia(mediaId) {
|
|
1002
|
+
return this.post(`/library/copy/${mediaId}`);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Download a media file (returns raw response for streaming)
|
|
1007
|
+
* @param {number} mediaId
|
|
1008
|
+
* @returns {Promise<Response>} Raw fetch response
|
|
1009
|
+
*/
|
|
1010
|
+
async downloadMedia(mediaId) {
|
|
1011
|
+
await this.ensureToken();
|
|
1012
|
+
const url = `${this.baseUrl}/api/library/download/${mediaId}`;
|
|
1013
|
+
const response = await fetch(url, {
|
|
1014
|
+
headers: { 'Authorization': `Bearer ${this.accessToken}` }
|
|
1015
|
+
});
|
|
1016
|
+
if (!response.ok) {
|
|
1017
|
+
const text = await response.text();
|
|
1018
|
+
throw new CmsApiError('GET', `/library/download/${mediaId}`, response.status, text);
|
|
1019
|
+
}
|
|
1020
|
+
return response;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Edit media properties
|
|
1025
|
+
* @param {number} mediaId
|
|
1026
|
+
* @param {Object} params - Properties to update (name, duration, retired, etc.)
|
|
1027
|
+
* @returns {Promise<Object>}
|
|
1028
|
+
*/
|
|
1029
|
+
async editMedia(mediaId, params) {
|
|
1030
|
+
return this.put(`/library/${mediaId}`, params);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Get media usage (which layouts/playlists reference this media)
|
|
1035
|
+
* @param {number} mediaId
|
|
1036
|
+
* @returns {Promise<Object>}
|
|
1037
|
+
*/
|
|
1038
|
+
async getMediaUsage(mediaId) {
|
|
1039
|
+
return this.get(`/library/usage/${mediaId}`);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Tidy the library (remove unused/old revisions)
|
|
1044
|
+
* @returns {Promise<void>}
|
|
1045
|
+
*/
|
|
1046
|
+
async tidyLibrary() {
|
|
1047
|
+
await this.post('/library/tidy');
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// ── Playlist CRUD (#35) ────────────────────────────────────────────
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* List playlists
|
|
1054
|
+
* @param {Object} [filters] - Filters (playlistId, name, etc.)
|
|
1055
|
+
* @returns {Promise<Array>}
|
|
1056
|
+
*/
|
|
1057
|
+
async listPlaylists(filters = {}) {
|
|
1058
|
+
const data = await this.get('/playlist', filters);
|
|
1059
|
+
return Array.isArray(data) ? data : [];
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* Create a playlist
|
|
1064
|
+
* @param {string} name
|
|
1065
|
+
* @returns {Promise<Object>}
|
|
1066
|
+
*/
|
|
1067
|
+
async createPlaylist(name) {
|
|
1068
|
+
return this.post('/playlist', { name });
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* Get a single playlist by ID
|
|
1073
|
+
* @param {number} playlistId
|
|
1074
|
+
* @returns {Promise<Object>}
|
|
1075
|
+
*/
|
|
1076
|
+
async getPlaylist(playlistId) {
|
|
1077
|
+
return this.get(`/playlist/${playlistId}`);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Edit playlist properties
|
|
1082
|
+
* @param {number} playlistId
|
|
1083
|
+
* @param {Object} params - Properties to update
|
|
1084
|
+
* @returns {Promise<Object>}
|
|
1085
|
+
*/
|
|
1086
|
+
async editPlaylist(playlistId, params) {
|
|
1087
|
+
return this.put(`/playlist/${playlistId}`, params);
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Delete a playlist
|
|
1092
|
+
* @param {number} playlistId
|
|
1093
|
+
* @returns {Promise<void>}
|
|
1094
|
+
*/
|
|
1095
|
+
async deletePlaylist(playlistId) {
|
|
1096
|
+
await this.del(`/playlist/${playlistId}`);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Reorder widgets in a playlist
|
|
1101
|
+
* @param {number} playlistId
|
|
1102
|
+
* @param {number[]} widgetIds - Ordered widget IDs
|
|
1103
|
+
* @returns {Promise<void>}
|
|
1104
|
+
*/
|
|
1105
|
+
async reorderPlaylist(playlistId, widgetIds) {
|
|
1106
|
+
await this.ensureToken();
|
|
1107
|
+
const url = `${this.baseUrl}/api/playlist/order/${playlistId}`;
|
|
1108
|
+
const urlParams = new URLSearchParams();
|
|
1109
|
+
for (const id of widgetIds) {
|
|
1110
|
+
urlParams.append('widgets[]', String(id));
|
|
1111
|
+
}
|
|
1112
|
+
const response = await fetch(url, {
|
|
1113
|
+
method: 'POST',
|
|
1114
|
+
headers: {
|
|
1115
|
+
'Authorization': `Bearer ${this.accessToken}`,
|
|
1116
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
1117
|
+
},
|
|
1118
|
+
body: urlParams
|
|
1119
|
+
});
|
|
1120
|
+
if (!response.ok) {
|
|
1121
|
+
const text = await response.text();
|
|
1122
|
+
throw new CmsApiError('POST', `/playlist/order/${playlistId}`, response.status, text);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
/**
|
|
1127
|
+
* Copy a playlist
|
|
1128
|
+
* @param {number} playlistId
|
|
1129
|
+
* @returns {Promise<Object>} Copied playlist
|
|
1130
|
+
*/
|
|
1131
|
+
async copyPlaylist(playlistId) {
|
|
1132
|
+
return this.post(`/playlist/copy/${playlistId}`);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// ── Widget Extras (#37) ────────────────────────────────────────────
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Set transition for a widget
|
|
1139
|
+
* @param {number} widgetId
|
|
1140
|
+
* @param {string} type - Transition type (e.g. 'fly', 'fade')
|
|
1141
|
+
* @param {Object} [config] - Transition config (duration, direction)
|
|
1142
|
+
* @returns {Promise<Object>}
|
|
1143
|
+
*/
|
|
1144
|
+
async setWidgetTransition(widgetId, type, config = {}) {
|
|
1145
|
+
return this.put(`/playlist/widget/transition/${widgetId}`, { type, ...config });
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Set audio for a widget
|
|
1150
|
+
* @param {number} widgetId
|
|
1151
|
+
* @param {Object} params - { mediaId, volume, loop }
|
|
1152
|
+
* @returns {Promise<Object>}
|
|
1153
|
+
*/
|
|
1154
|
+
async setWidgetAudio(widgetId, params) {
|
|
1155
|
+
return this.put(`/playlist/widget/${widgetId}/audio`, params);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Remove audio from a widget
|
|
1160
|
+
* @param {number} widgetId
|
|
1161
|
+
* @returns {Promise<void>}
|
|
1162
|
+
*/
|
|
1163
|
+
async removeWidgetAudio(widgetId) {
|
|
1164
|
+
await this.del(`/playlist/widget/${widgetId}/audio`);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Set expiry dates for a widget
|
|
1169
|
+
* @param {number} widgetId
|
|
1170
|
+
* @param {Object} params - { fromDt, toDt, deleteOnExpiry }
|
|
1171
|
+
* @returns {Promise<Object>}
|
|
1172
|
+
*/
|
|
1173
|
+
async setWidgetExpiry(widgetId, params) {
|
|
1174
|
+
return this.put(`/playlist/widget/${widgetId}/expiry`, params);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// ── Template Save / Manage (#39) ───────────────────────────────────
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Save a layout as a template
|
|
1181
|
+
* @param {number} layoutId
|
|
1182
|
+
* @param {Object} params - { name, description, includeWidgets }
|
|
1183
|
+
* @returns {Promise<Object>}
|
|
1184
|
+
*/
|
|
1185
|
+
async saveAsTemplate(layoutId, params) {
|
|
1186
|
+
return this.post(`/template/${layoutId}`, params);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Get a single template by ID
|
|
1191
|
+
* @param {number} templateId
|
|
1192
|
+
* @returns {Promise<Object>}
|
|
1193
|
+
*/
|
|
1194
|
+
async getTemplate(templateId) {
|
|
1195
|
+
return this.get(`/template/${templateId}`);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* Delete a template
|
|
1200
|
+
* @param {number} templateId
|
|
1201
|
+
* @returns {Promise<void>}
|
|
1202
|
+
*/
|
|
1203
|
+
async deleteTemplate(templateId) {
|
|
1204
|
+
await this.del(`/template/${templateId}`);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// ── Dataset CRUD (#28) ─────────────────────────────────────────────
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* List datasets
|
|
1211
|
+
* @param {Object} [filters] - Filters (dataSetId, dataSet)
|
|
1212
|
+
* @returns {Promise<Array>}
|
|
1213
|
+
*/
|
|
1214
|
+
async listDatasets(filters = {}) {
|
|
1215
|
+
const data = await this.get('/dataset', filters);
|
|
1216
|
+
return Array.isArray(data) ? data : [];
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* Create a dataset
|
|
1221
|
+
* @param {Object} params - { dataSet, description }
|
|
1222
|
+
* @returns {Promise<Object>}
|
|
1223
|
+
*/
|
|
1224
|
+
async createDataset(params) {
|
|
1225
|
+
return this.post('/dataset', params);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Edit a dataset
|
|
1230
|
+
* @param {number} dataSetId
|
|
1231
|
+
* @param {Object} params
|
|
1232
|
+
* @returns {Promise<Object>}
|
|
1233
|
+
*/
|
|
1234
|
+
async editDataset(dataSetId, params) {
|
|
1235
|
+
return this.put(`/dataset/${dataSetId}`, params);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Delete a dataset
|
|
1240
|
+
* @param {number} dataSetId
|
|
1241
|
+
* @returns {Promise<void>}
|
|
1242
|
+
*/
|
|
1243
|
+
async deleteDataset(dataSetId) {
|
|
1244
|
+
await this.del(`/dataset/${dataSetId}`);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* List columns in a dataset
|
|
1249
|
+
* @param {number} dataSetId
|
|
1250
|
+
* @returns {Promise<Array>}
|
|
1251
|
+
*/
|
|
1252
|
+
async listDatasetColumns(dataSetId) {
|
|
1253
|
+
const data = await this.get(`/dataset/${dataSetId}/column`);
|
|
1254
|
+
return Array.isArray(data) ? data : [];
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Create a column in a dataset
|
|
1259
|
+
* @param {number} dataSetId
|
|
1260
|
+
* @param {Object} params - { heading, dataTypeId, dataSetColumnTypeId, listContent, ... }
|
|
1261
|
+
* @returns {Promise<Object>}
|
|
1262
|
+
*/
|
|
1263
|
+
async createDatasetColumn(dataSetId, params) {
|
|
1264
|
+
return this.post(`/dataset/${dataSetId}/column`, params);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/**
|
|
1268
|
+
* Edit a dataset column
|
|
1269
|
+
* @param {number} dataSetId
|
|
1270
|
+
* @param {number} columnId
|
|
1271
|
+
* @param {Object} params
|
|
1272
|
+
* @returns {Promise<Object>}
|
|
1273
|
+
*/
|
|
1274
|
+
async editDatasetColumn(dataSetId, columnId, params) {
|
|
1275
|
+
return this.put(`/dataset/${dataSetId}/column/${columnId}`, params);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Delete a dataset column
|
|
1280
|
+
* @param {number} dataSetId
|
|
1281
|
+
* @param {number} columnId
|
|
1282
|
+
* @returns {Promise<void>}
|
|
1283
|
+
*/
|
|
1284
|
+
async deleteDatasetColumn(dataSetId, columnId) {
|
|
1285
|
+
await this.del(`/dataset/${dataSetId}/column/${columnId}`);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* List rows (data) in a dataset
|
|
1290
|
+
* @param {number} dataSetId
|
|
1291
|
+
* @param {Object} [filters] - Filters (page, length)
|
|
1292
|
+
* @returns {Promise<Array>}
|
|
1293
|
+
*/
|
|
1294
|
+
async listDatasetData(dataSetId, filters = {}) {
|
|
1295
|
+
const data = await this.get(`/dataset/data/${dataSetId}`, filters);
|
|
1296
|
+
return Array.isArray(data) ? data : [];
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
/**
|
|
1300
|
+
* Add a row to a dataset
|
|
1301
|
+
* @param {number} dataSetId
|
|
1302
|
+
* @param {Object} rowData - Column heading → value pairs
|
|
1303
|
+
* @returns {Promise<Object>}
|
|
1304
|
+
*/
|
|
1305
|
+
async addDatasetRow(dataSetId, rowData) {
|
|
1306
|
+
return this.post(`/dataset/data/${dataSetId}`, rowData);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* Edit a dataset row
|
|
1311
|
+
* @param {number} dataSetId
|
|
1312
|
+
* @param {number} rowId
|
|
1313
|
+
* @param {Object} rowData - Column heading → value pairs
|
|
1314
|
+
* @returns {Promise<Object>}
|
|
1315
|
+
*/
|
|
1316
|
+
async editDatasetRow(dataSetId, rowId, rowData) {
|
|
1317
|
+
return this.put(`/dataset/data/${dataSetId}/${rowId}`, rowData);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/**
|
|
1321
|
+
* Delete a dataset row
|
|
1322
|
+
* @param {number} dataSetId
|
|
1323
|
+
* @param {number} rowId
|
|
1324
|
+
* @returns {Promise<void>}
|
|
1325
|
+
*/
|
|
1326
|
+
async deleteDatasetRow(dataSetId, rowId) {
|
|
1327
|
+
await this.del(`/dataset/data/${dataSetId}/${rowId}`);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* Import CSV data into a dataset
|
|
1332
|
+
* @param {number} dataSetId
|
|
1333
|
+
* @param {FormData} formData - Must include CSV file
|
|
1334
|
+
* @returns {Promise<Object>}
|
|
1335
|
+
*/
|
|
1336
|
+
async importDatasetCsv(dataSetId, formData) {
|
|
1337
|
+
return this.requestMultipart('POST', `/dataset/import/${dataSetId}`, formData);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Clear all data from a dataset
|
|
1342
|
+
* @param {number} dataSetId
|
|
1343
|
+
* @returns {Promise<void>}
|
|
1344
|
+
*/
|
|
1345
|
+
async clearDataset(dataSetId) {
|
|
1346
|
+
await this.del(`/dataset/data/${dataSetId}`);
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// ── Notification CRUD (#29) ────────────────────────────────────────
|
|
1350
|
+
|
|
1351
|
+
/**
|
|
1352
|
+
* List notifications
|
|
1353
|
+
* @param {Object} [filters] - Filters (notificationId, subject)
|
|
1354
|
+
* @returns {Promise<Array>}
|
|
1355
|
+
*/
|
|
1356
|
+
async listNotifications(filters = {}) {
|
|
1357
|
+
const data = await this.get('/notification', filters);
|
|
1358
|
+
return Array.isArray(data) ? data : [];
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Create a notification
|
|
1363
|
+
* @param {Object} params - { subject, body, isEmail, isInterrupt, displayGroupIds, ... }
|
|
1364
|
+
* @returns {Promise<Object>}
|
|
1365
|
+
*/
|
|
1366
|
+
async createNotification(params) {
|
|
1367
|
+
return this.post('/notification', params);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
/**
|
|
1371
|
+
* Edit a notification
|
|
1372
|
+
* @param {number} notificationId
|
|
1373
|
+
* @param {Object} params
|
|
1374
|
+
* @returns {Promise<Object>}
|
|
1375
|
+
*/
|
|
1376
|
+
async editNotification(notificationId, params) {
|
|
1377
|
+
return this.put(`/notification/${notificationId}`, params);
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* Delete a notification
|
|
1382
|
+
* @param {number} notificationId
|
|
1383
|
+
* @returns {Promise<void>}
|
|
1384
|
+
*/
|
|
1385
|
+
async deleteNotification(notificationId) {
|
|
1386
|
+
await this.del(`/notification/${notificationId}`);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
// ── Folder CRUD (#30) ─────────────────────────────────────────────
|
|
1390
|
+
|
|
1391
|
+
/**
|
|
1392
|
+
* List folders
|
|
1393
|
+
* @param {Object} [filters] - Filters (folderId, text)
|
|
1394
|
+
* @returns {Promise<Array>}
|
|
1395
|
+
*/
|
|
1396
|
+
async listFolders(filters = {}) {
|
|
1397
|
+
const data = await this.get('/folder', filters);
|
|
1398
|
+
return Array.isArray(data) ? data : [];
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/**
|
|
1402
|
+
* Create a folder
|
|
1403
|
+
* @param {Object} params - { text, parentId }
|
|
1404
|
+
* @returns {Promise<Object>}
|
|
1405
|
+
*/
|
|
1406
|
+
async createFolder(params) {
|
|
1407
|
+
return this.post('/folder', params);
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
/**
|
|
1411
|
+
* Edit a folder
|
|
1412
|
+
* @param {number} folderId
|
|
1413
|
+
* @param {Object} params
|
|
1414
|
+
* @returns {Promise<Object>}
|
|
1415
|
+
*/
|
|
1416
|
+
async editFolder(folderId, params) {
|
|
1417
|
+
return this.put(`/folder/${folderId}`, params);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Delete a folder
|
|
1422
|
+
* @param {number} folderId
|
|
1423
|
+
* @returns {Promise<void>}
|
|
1424
|
+
*/
|
|
1425
|
+
async deleteFolder(folderId) {
|
|
1426
|
+
await this.del(`/folder/${folderId}`);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
// ── Tag CRUD + Entity Tagging (#31) ────────────────────────────────
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* List tags
|
|
1433
|
+
* @param {Object} [filters] - Filters (tagId, tag)
|
|
1434
|
+
* @returns {Promise<Array>}
|
|
1435
|
+
*/
|
|
1436
|
+
async listTags(filters = {}) {
|
|
1437
|
+
const data = await this.get('/tag', filters);
|
|
1438
|
+
return Array.isArray(data) ? data : [];
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* Create a tag
|
|
1443
|
+
* @param {Object} params - { tag }
|
|
1444
|
+
* @returns {Promise<Object>}
|
|
1445
|
+
*/
|
|
1446
|
+
async createTag(params) {
|
|
1447
|
+
return this.post('/tag', params);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
/**
|
|
1451
|
+
* Edit a tag
|
|
1452
|
+
* @param {number} tagId
|
|
1453
|
+
* @param {Object} params
|
|
1454
|
+
* @returns {Promise<Object>}
|
|
1455
|
+
*/
|
|
1456
|
+
async editTag(tagId, params) {
|
|
1457
|
+
return this.put(`/tag/${tagId}`, params);
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* Delete a tag
|
|
1462
|
+
* @param {number} tagId
|
|
1463
|
+
* @returns {Promise<void>}
|
|
1464
|
+
*/
|
|
1465
|
+
async deleteTag(tagId) {
|
|
1466
|
+
await this.del(`/tag/${tagId}`);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* Tag an entity (layout, media, campaign, etc.)
|
|
1471
|
+
* @param {string} entity - Entity type (layout, media, campaign, displaygroup, playlist)
|
|
1472
|
+
* @param {number} id - Entity ID
|
|
1473
|
+
* @param {string[]} tags - Tag names
|
|
1474
|
+
* @returns {Promise<void>}
|
|
1475
|
+
*/
|
|
1476
|
+
async tagEntity(entity, id, tags) {
|
|
1477
|
+
await this.post(`/${entity}/${id}/tag`, { tag: tags.join(',') });
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
* Remove tags from an entity
|
|
1482
|
+
* @param {string} entity - Entity type
|
|
1483
|
+
* @param {number} id - Entity ID
|
|
1484
|
+
* @param {string[]} tags - Tag names to remove
|
|
1485
|
+
* @returns {Promise<void>}
|
|
1486
|
+
*/
|
|
1487
|
+
async untagEntity(entity, id, tags) {
|
|
1488
|
+
await this.post(`/${entity}/${id}/untag`, { tag: tags.join(',') });
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// ── DisplayGroup Actions (#32) ─────────────────────────────────────
|
|
1492
|
+
|
|
1493
|
+
/**
|
|
1494
|
+
* Change the layout on a display group (immediate override)
|
|
1495
|
+
* @param {number} displayGroupId
|
|
1496
|
+
* @param {number} layoutId
|
|
1497
|
+
* @returns {Promise<void>}
|
|
1498
|
+
*/
|
|
1499
|
+
async dgChangeLayout(displayGroupId, layoutId) {
|
|
1500
|
+
await this.post(`/displaygroup/${displayGroupId}/action/changeLayout`, { layoutId });
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Overlay a layout on a display group
|
|
1505
|
+
* @param {number} displayGroupId
|
|
1506
|
+
* @param {number} layoutId
|
|
1507
|
+
* @returns {Promise<void>}
|
|
1508
|
+
*/
|
|
1509
|
+
async dgOverlayLayout(displayGroupId, layoutId) {
|
|
1510
|
+
await this.post(`/displaygroup/${displayGroupId}/action/overlayLayout`, { layoutId });
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Revert a display group to its scheduled content
|
|
1515
|
+
* @param {number} displayGroupId
|
|
1516
|
+
* @returns {Promise<void>}
|
|
1517
|
+
*/
|
|
1518
|
+
async dgRevertToSchedule(displayGroupId) {
|
|
1519
|
+
await this.post(`/displaygroup/${displayGroupId}/action/revertToSchedule`);
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
/**
|
|
1523
|
+
* Trigger immediate content collection on a display group
|
|
1524
|
+
* @param {number} displayGroupId
|
|
1525
|
+
* @returns {Promise<void>}
|
|
1526
|
+
*/
|
|
1527
|
+
async dgCollectNow(displayGroupId) {
|
|
1528
|
+
await this.post(`/displaygroup/${displayGroupId}/action/collectNow`);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/**
|
|
1532
|
+
* Send a command to a display group
|
|
1533
|
+
* @param {number} displayGroupId
|
|
1534
|
+
* @param {number} commandId
|
|
1535
|
+
* @returns {Promise<void>}
|
|
1536
|
+
*/
|
|
1537
|
+
async dgSendCommand(displayGroupId, commandId) {
|
|
1538
|
+
await this.post(`/displaygroup/${displayGroupId}/action/command`, { commandId });
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Edit display group properties
|
|
1543
|
+
* @param {number} displayGroupId
|
|
1544
|
+
* @param {Object} params - Properties to update
|
|
1545
|
+
* @returns {Promise<Object>}
|
|
1546
|
+
*/
|
|
1547
|
+
async editDisplayGroup(displayGroupId, params) {
|
|
1548
|
+
return this.put(`/displaygroup/${displayGroupId}`, params);
|
|
1549
|
+
}
|
|
750
1550
|
}
|
|
751
1551
|
|
|
752
1552
|
/**
|